Skip to content

Commit

Permalink
Make JavaPackage implement HasAnnotations
Browse files Browse the repository at this point in the history
Issue TNG#263

Signed-off-by: Roland Weisleder <[email protected]>
  • Loading branch information
rweisleder authored and codecholeric committed Apr 7, 2020
1 parent 9a9bebc commit 34c9137
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ public final class JavaAnnotation<OWNER extends HasDescription> implements HasTy
private final String description;
private final Map<String, Object> values;

private JavaAnnotation(JavaClass type, OWNER owner, CanBeAnnotated annotatedElement, String description, Map<String, Object> values) {
this.type = checkNotNull(type);
this.owner = checkNotNull(owner);
this.annotatedElement = checkNotNull(annotatedElement);
this.description = checkNotNull(description);
this.values = checkNotNull(values);
}

JavaAnnotation(OWNER owner, JavaAnnotationBuilder builder) {
this.type = checkNotNull(builder.getType());
this.owner = checkNotNull(owner);
Expand All @@ -88,7 +96,7 @@ public final class JavaAnnotation<OWNER extends HasDescription> implements HasTy
this.values = checkNotNull(builder.getValues(this));
}

private CanBeAnnotated getAnnotatedElement(Object owner) {
private static CanBeAnnotated getAnnotatedElement(Object owner) {
Object candiate = owner;
while (!(candiate instanceof JavaClass) && !(candiate instanceof JavaMember) && (candiate instanceof HasOwner<?>)) {
candiate = ((HasOwner<?>) candiate).getOwner();
Expand Down Expand Up @@ -135,6 +143,10 @@ public OWNER getOwner() {
return owner;
}

<NEW_OWNER extends HasDescription> JavaAnnotation<NEW_OWNER> withOwner(NEW_OWNER newOwner) {
return new JavaAnnotation<>(type, newOwner, annotatedElement, description, values);
}

/**
* Returns either the element annotated with this {@link JavaAnnotation} (a class or member)
* or in case this annotation is an annotation parameter, the element annotated with an
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.tngtech.archunit.core.domain;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
Expand All @@ -24,13 +25,15 @@
import java.util.Set;

import com.google.common.base.Splitter;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.ChainableFunction;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.base.Function;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.base.Predicate;
import com.tngtech.archunit.core.domain.properties.HasAnnotations;
Expand All @@ -42,15 +45,17 @@
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
import static com.tngtech.archunit.PublicAPI.Usage.INHERITANCE;
import static com.tngtech.archunit.base.DescribedPredicate.equalTo;
import static com.tngtech.archunit.base.Guava.toGuava;
import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_SIMPLE_NAME;
import static com.tngtech.archunit.core.domain.properties.HasName.Functions.GET_NAME;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;

public final class JavaPackage implements HasName {
public final class JavaPackage implements HasName, HasAnnotations<JavaPackage> {
private final String name;
private final String relativeName;
private final Set<JavaClass> classes;
private final Optional<? extends HasAnnotations> packageInfo;
private final Optional<JavaClass> packageInfo;
private final Map<String, JavaPackage> subPackages;
private Optional<JavaPackage> parent = Optional.absent();

Expand Down Expand Up @@ -80,16 +85,110 @@ public String getRelativeName() {
}

@PublicAPI(usage = ACCESS)
public HasAnnotations getPackageInfo() {
public HasAnnotations<?> getPackageInfo() {
return tryGetPackageInfo().getOrThrow(
new IllegalArgumentException(String.format("Package %s does not contain a package-info.java", getName())));
}

@PublicAPI(usage = ACCESS)
public Optional<? extends HasAnnotations> tryGetPackageInfo() {
public Optional<? extends HasAnnotations<?>> tryGetPackageInfo() {
return packageInfo;
}

@Override
@PublicAPI(usage = ACCESS)
public Set<? extends JavaAnnotation<JavaPackage>> getAnnotations() {
if (packageInfo.isPresent()) {
return FluentIterable.from(packageInfo.get().getAnnotations()).transform(toGuava(withSelfAsOwner)).toSet();
}
return emptySet();
}

@Override
@PublicAPI(usage = ACCESS)
public <A extends Annotation> A getAnnotationOfType(Class<A> type) {
return getAnnotationOfType(type.getName()).as(type);
}

@Override
@PublicAPI(usage = ACCESS)
public JavaAnnotation<JavaPackage> getAnnotationOfType(String typeName) {
return tryGetAnnotationOfType(typeName).getOrThrow(new IllegalArgumentException(
String.format("Package %s is not annotated with @%s", getName(), typeName)));
}

@Override
@PublicAPI(usage = ACCESS)
public <A extends Annotation> Optional<A> tryGetAnnotationOfType(Class<A> type) {
if (packageInfo.isPresent()) {
return packageInfo.get().tryGetAnnotationOfType(type);
}
return Optional.absent();
}

@Override
@PublicAPI(usage = ACCESS)
public Optional<JavaAnnotation<JavaPackage>> tryGetAnnotationOfType(String typeName) {
if (packageInfo.isPresent()) {
return packageInfo.get().tryGetAnnotationOfType(typeName).transform(withSelfAsOwner);
}
return Optional.absent();
}

@Override
@PublicAPI(usage = ACCESS)
public boolean isAnnotatedWith(Class<? extends Annotation> annotationType) {
if (packageInfo.isPresent()) {
return packageInfo.get().isAnnotatedWith(annotationType);
}
return false;
}

@Override
@PublicAPI(usage = ACCESS)
public boolean isAnnotatedWith(String annotationTypeName) {
if (packageInfo.isPresent()) {
return packageInfo.get().isAnnotatedWith(annotationTypeName);
}
return false;
}

@Override
@PublicAPI(usage = ACCESS)
public boolean isAnnotatedWith(DescribedPredicate<? super JavaAnnotation<?>> predicate) {
if (packageInfo.isPresent()) {
return packageInfo.get().isAnnotatedWith(predicate);
}
return false;
}

@Override
@PublicAPI(usage = ACCESS)
public boolean isMetaAnnotatedWith(Class<? extends Annotation> annotationType) {
if (packageInfo.isPresent()) {
return packageInfo.get().isMetaAnnotatedWith(annotationType);
}
return false;
}

@Override
@PublicAPI(usage = ACCESS)
public boolean isMetaAnnotatedWith(String annotationTypeName) {
if (packageInfo.isPresent()) {
return packageInfo.get().isMetaAnnotatedWith(annotationTypeName);
}
return false;
}

@Override
@PublicAPI(usage = ACCESS)
public boolean isMetaAnnotatedWith(DescribedPredicate<? super JavaAnnotation<?>> predicate) {
if (packageInfo.isPresent()) {
return packageInfo.get().isMetaAnnotatedWith(predicate);
}
return false;
}

/**
* @return the parent package, e.g. {@code java} for package {@code java.lang}
*/
Expand Down Expand Up @@ -387,11 +486,24 @@ public void accept(Predicate<? super JavaPackage> predicate, PackageVisitor visi
}
}

@Override
public String getDescription() {
return "Package <" + name + ">";
}

@Override
public String toString() {
return getClass().getSimpleName() + "[" + getName() + "]";
}

private final Function<? super JavaAnnotation<JavaClass>, JavaAnnotation<JavaPackage>> withSelfAsOwner =
new Function<JavaAnnotation<JavaClass>, JavaAnnotation<JavaPackage>>() {
@Override
public JavaAnnotation<JavaPackage> apply(JavaAnnotation<JavaClass> input) {
return input.withOwner(JavaPackage.this);
}
};

static JavaPackage simple(JavaClass javaClass) {
String packageName = javaClass.getPackageName();
JavaPackage defaultPackage = JavaPackage.from(singleton(javaClass));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.tngtech.archunit.base.Predicate;
import com.tngtech.archunit.core.domain.JavaPackage.ClassVisitor;
import com.tngtech.archunit.core.domain.JavaPackage.PackageVisitor;
import com.tngtech.archunit.core.domain.packageexamples.annotated.PackageLevelAnnotation;
import com.tngtech.archunit.core.domain.packageexamples.first.First1;
import com.tngtech.archunit.core.domain.packageexamples.first.First2;
import com.tngtech.archunit.core.domain.packageexamples.second.ClassDependingOnOtherSecondClass;
Expand All @@ -28,6 +29,7 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static com.google.common.collect.Iterables.getOnlyElement;
import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_SIMPLE_NAME;
import static com.tngtech.archunit.core.domain.JavaPackage.Functions.GET_CLASSES;
import static com.tngtech.archunit.core.domain.JavaPackage.Functions.GET_RELATIVE_NAME;
Expand Down Expand Up @@ -101,6 +103,7 @@ public void creates_single_package() {
JavaPackage javaPackage = defaultPackage.getPackage("java.lang");

assertThat(javaPackage.getName()).isEqualTo("java.lang");
assertThat(javaPackage.getDescription()).isEqualTo("Package <java.lang>");
assertThat(javaPackage.getRelativeName()).isEqualTo("lang");
assertThatClasses(javaPackage.getClasses()).contain(Object.class, String.class);
}
Expand Down Expand Up @@ -346,6 +349,120 @@ public void test_tryGetPackageInfo() {
assertThat(notAnnotatedPackage.tryGetPackageInfo()).isAbsent();
}

@Test
public void test_getAnnotations() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage notAnnotatedPackage = importPackage("packageexamples");

JavaAnnotation<JavaPackage> annotation = getOnlyElement(annotatedPackage.getAnnotations());
assertThat(annotation.getRawType()).matches(PackageLevelAnnotation.class);
assertThat(annotation.getOwner()).isEqualTo(annotatedPackage);

assertThat(notAnnotatedPackage.getAnnotations()).isEmpty();
}

@Test
public void test_getAnnotationOfType_type() {
final JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
final JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.getAnnotationOfType(PackageLevelAnnotation.class)).isInstanceOf(PackageLevelAnnotation.class);

assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
@Override
public void call() {
annotatedPackage.getAnnotationOfType(Deprecated.class);
}
}).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(".packageexamples.annotated is not annotated with @");

assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
@Override
public void call() {
notAnnotatedPackage.getAnnotationOfType(Deprecated.class);
}
}).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(".packageexamples is not annotated with @");
}

@Test
public void test_getAnnotationOfType_typeName() {
final JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
final JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.getAnnotationOfType("com.tngtech.archunit.core.domain.packageexamples.annotated.PackageLevelAnnotation")
.getRawType()).matches(PackageLevelAnnotation.class);

assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
@Override
public void call() {
annotatedPackage.getAnnotationOfType("java.lang.Deprecated");
}
}).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(".packageexamples.annotated is not annotated with @");

assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
@Override
public void call() {
notAnnotatedPackage.getAnnotationOfType("java.lang.Deprecated");
}
}).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(".packageexamples is not annotated with @");
}

@Test
public void test_tryGetAnnotationOfType_type() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.tryGetAnnotationOfType(PackageLevelAnnotation.class)).isPresent();
assertThat(annotatedPackage.tryGetAnnotationOfType(Deprecated.class)).isAbsent();

assertThat(notAnnotatedPackage.tryGetAnnotationOfType(Deprecated.class)).isAbsent();
}

@Test
public void test_tryGetAnnotationOfType_typeName() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.tryGetAnnotationOfType(
"com.tngtech.archunit.core.domain.packageexamples.annotated.PackageLevelAnnotation")).isPresent();
assertThat(annotatedPackage.tryGetAnnotationOfType("java.lang.Deprecated")).isAbsent();

assertThat(notAnnotatedPackage.tryGetAnnotationOfType("java.lang.Deprecated")).isAbsent();
}

@Test
public void test_isAnnotatedWith_type() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.isAnnotatedWith(PackageLevelAnnotation.class)).isTrue();
assertThat(annotatedPackage.isAnnotatedWith(Deprecated.class)).isFalse();

assertThat(notAnnotatedPackage.isAnnotatedWith(Deprecated.class)).isFalse();
}

@Test
public void test_isAnnotatedWith_typeName() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.isAnnotatedWith("com.tngtech.archunit.core.domain.packageexamples.annotated.PackageLevelAnnotation")).isTrue();
assertThat(annotatedPackage.isAnnotatedWith("java.lang.Deprecated")).isFalse();

assertThat(notAnnotatedPackage.isAnnotatedWith("java.lang.Deprecated")).isFalse();
}

@Test
public void test_isAnnotatedWith_predicate() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage notAnnotatedPackage = importPackage("packageexamples");

assertThat(annotatedPackage.isAnnotatedWith(DescribedPredicate.<JavaAnnotation<?>>alwaysTrue())).isTrue();
assertThat(annotatedPackage.isAnnotatedWith(DescribedPredicate.<JavaAnnotation<?>>alwaysFalse())).isFalse();

assertThat(notAnnotatedPackage.isAnnotatedWith(DescribedPredicate.<JavaAnnotation<?>>alwaysTrue())).isFalse();
assertThat(notAnnotatedPackage.isAnnotatedWith(DescribedPredicate.<JavaAnnotation<?>>alwaysFalse())).isFalse();
}

@Test
public void function_GET_RELATIVE_NAME() {
JavaPackage defaultPackage = importDefaultPackage(Object.class);
Expand Down Expand Up @@ -406,4 +523,4 @@ private JavaPackage importPackage(String subPackageName) {
JavaClasses classes = new ClassFileImporter().importPackages(packageName);
return classes.getPackage(packageName);
}
}
}

0 comments on commit 34c9137

Please sign in to comment.