com.tngtech.archunit.core.domain.ReverseDependencies Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of archunit Show documentation
Show all versions of archunit Show documentation
A Java architecture test library, to specify and assert architecture rules in plain Java - Module 'archunit'
/*
* Copyright 2014-2024 TNG Technology Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tngtech.archunit.core.domain;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.tngtech.archunit.base.Suppliers;
final class ReverseDependencies {
private final LoadingCache> accessToFieldCache;
private final LoadingCache> callToMethodCache;
private final LoadingCache> referenceToMethodCache;
private final LoadingCache> callToConstructorCache;
private final LoadingCache> referenceToConstructorCache;
private final SetMultimap fieldTypeDependencies;
private final SetMultimap methodParameterTypeDependencies;
private final SetMultimap methodReturnTypeDependencies;
private final SetMultimap> methodsThrowsDeclarationDependencies;
private final SetMultimap constructorParameterTypeDependencies;
private final SetMultimap> constructorThrowsDeclarationDependencies;
private final SetMultimap> annotationTypeDependencies;
private final SetMultimap> annotationParameterTypeDependencies;
private final SetMultimap instanceofCheckDependencies;
private final Supplier> directDependenciesToClass;
private ReverseDependencies(ReverseDependencies.Creation creation) {
accessToFieldCache = CacheBuilder.newBuilder().build(new ResolvingAccessLoader<>(creation.fieldAccessDependencies.build()));
callToMethodCache = CacheBuilder.newBuilder().build(new ResolvingAccessLoader<>(creation.methodCallDependencies.build()));
referenceToMethodCache = CacheBuilder.newBuilder().build(new ResolvingAccessLoader<>(creation.methodReferenceDependencies.build()));
callToConstructorCache = CacheBuilder.newBuilder().build(new ConstructorAccessLoader<>(creation.constructorCallDependencies.build()));
referenceToConstructorCache = CacheBuilder.newBuilder().build(new ConstructorAccessLoader<>(creation.constructorReferenceDependencies.build()));
this.fieldTypeDependencies = creation.fieldTypeDependencies.build();
this.methodParameterTypeDependencies = creation.methodParameterTypeDependencies.build();
this.methodReturnTypeDependencies = creation.methodReturnTypeDependencies.build();
this.methodsThrowsDeclarationDependencies = creation.methodsThrowsDeclarationDependencies.build();
this.constructorParameterTypeDependencies = creation.constructorParameterTypeDependencies.build();
this.constructorThrowsDeclarationDependencies = creation.constructorThrowsDeclarationDependencies.build();
this.annotationTypeDependencies = creation.annotationTypeDependencies.build();
this.annotationParameterTypeDependencies = creation.annotationParameterTypeDependencies.build();
this.instanceofCheckDependencies = creation.instanceofCheckDependencies.build();
this.directDependenciesToClass = createDirectDependenciesToClassSupplier(creation.allDependencies);
}
private static Supplier> createDirectDependenciesToClassSupplier(List allDependencies) {
return Suppliers.memoize(() -> {
ImmutableSetMultimap.Builder result = ImmutableSetMultimap.builder();
for (JavaClassDependencies dependencies : allDependencies) {
for (Dependency dependency : dependencies.getDirectDependenciesFromClass()) {
result.put(dependency.getTargetClass(), dependency);
}
}
return result.build();
});
}
Set getAccessesTo(JavaField field) {
return accessToFieldCache.getUnchecked(field);
}
Set getCallsTo(JavaMethod method) {
return callToMethodCache.getUnchecked(method);
}
Set getReferencesTo(JavaMethod method) {
return referenceToMethodCache.getUnchecked(method);
}
Set getCallsTo(JavaConstructor constructor) {
return callToConstructorCache.getUnchecked(constructor);
}
Set getReferencesTo(JavaConstructor constructor) {
return referenceToConstructorCache.getUnchecked(constructor);
}
Set getFieldsWithTypeOf(JavaClass clazz) {
return fieldTypeDependencies.get(clazz);
}
Set getMethodsWithParameterTypeOf(JavaClass clazz) {
return methodParameterTypeDependencies.get(clazz);
}
Set getMethodsWithReturnTypeOf(JavaClass clazz) {
return methodReturnTypeDependencies.get(clazz);
}
Set> getMethodThrowsDeclarationsWithTypeOf(JavaClass clazz) {
return methodsThrowsDeclarationDependencies.get(clazz);
}
Set getConstructorsWithParameterTypeOf(JavaClass clazz) {
return constructorParameterTypeDependencies.get(clazz);
}
Set> getConstructorsWithThrowsDeclarationTypeOf(JavaClass clazz) {
return constructorThrowsDeclarationDependencies.get(clazz);
}
Set> getAnnotationsWithTypeOf(JavaClass clazz) {
return annotationTypeDependencies.get(clazz);
}
Set> getAnnotationsWithParameterTypeOf(JavaClass clazz) {
return annotationParameterTypeDependencies.get(clazz);
}
Set getInstanceofChecksWithTypeOf(JavaClass clazz) {
return instanceofCheckDependencies.get(clazz);
}
Set getDirectDependenciesTo(JavaClass clazz) {
return directDependenciesToClass.get().get(clazz);
}
static final ReverseDependencies EMPTY = new ReverseDependencies(new Creation());
static class Creation {
private final ImmutableSetMultimap.Builder fieldAccessDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder methodCallDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder methodReferenceDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder constructorCallDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder constructorReferenceDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder fieldTypeDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder methodParameterTypeDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder methodReturnTypeDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder> methodsThrowsDeclarationDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder constructorParameterTypeDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder> constructorThrowsDeclarationDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder> annotationTypeDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder> annotationParameterTypeDependencies = ImmutableSetMultimap.builder();
private final ImmutableSetMultimap.Builder instanceofCheckDependencies = ImmutableSetMultimap.builder();
private final List allDependencies = new ArrayList<>();
public void registerDependenciesOf(JavaClass clazz, JavaClassDependencies classDependencies) {
registerAccesses(clazz);
registerFields(clazz);
registerMethods(clazz);
registerConstructors(clazz);
registerAnnotations(clazz);
registerStaticInitializer(clazz);
allDependencies.add(classDependencies);
}
private void registerAccesses(JavaClass clazz) {
for (JavaFieldAccess access : clazz.getFieldAccessesFromSelf()) {
fieldAccessDependencies.put(access.getTargetOwner(), access);
}
for (JavaMethodCall call : clazz.getMethodCallsFromSelf()) {
methodCallDependencies.put(call.getTargetOwner(), call);
}
for (JavaMethodReference reference : clazz.getMethodReferencesFromSelf()) {
methodReferenceDependencies.put(reference.getTargetOwner(), reference);
}
for (JavaConstructorCall call : clazz.getConstructorCallsFromSelf()) {
constructorCallDependencies.put(call.getTarget().getFullName(), call);
}
for (JavaConstructorReference reference : clazz.getConstructorReferencesFromSelf()) {
constructorReferenceDependencies.put(reference.getTarget().getFullName(), reference);
}
}
private void registerFields(JavaClass clazz) {
for (JavaField field : clazz.getFields()) {
fieldTypeDependencies.put(field.getRawType(), field);
}
}
private void registerMethods(JavaClass clazz) {
for (JavaMethod method : clazz.getMethods()) {
for (JavaClass parameter : method.getRawParameterTypes()) {
methodParameterTypeDependencies.put(parameter, method);
}
methodReturnTypeDependencies.put(method.getRawReturnType(), method);
for (ThrowsDeclaration throwsDeclaration : method.getThrowsClause()) {
methodsThrowsDeclarationDependencies.put(throwsDeclaration.getRawType(), throwsDeclaration);
}
for (InstanceofCheck instanceofCheck : method.getInstanceofChecks()) {
instanceofCheckDependencies.put(instanceofCheck.getRawType(), instanceofCheck);
}
}
}
private void registerConstructors(JavaClass clazz) {
for (JavaConstructor constructor : clazz.getConstructors()) {
for (JavaClass parameter : constructor.getRawParameterTypes()) {
constructorParameterTypeDependencies.put(parameter, constructor);
}
for (ThrowsDeclaration throwsDeclaration : constructor.getThrowsClause()) {
constructorThrowsDeclarationDependencies.put(throwsDeclaration.getRawType(), throwsDeclaration);
}
for (InstanceofCheck instanceofCheck : constructor.getInstanceofChecks()) {
instanceofCheckDependencies.put(instanceofCheck.getRawType(), instanceofCheck);
}
}
}
private void registerAnnotations(JavaClass clazz) {
for (JavaAnnotation> annotation : findAnnotations(clazz)) {
annotationTypeDependencies.put(annotation.getRawType(), annotation);
annotation.accept(new JavaAnnotation.DefaultParameterVisitor() {
@Override
public void visitClass(String propertyName, JavaClass javaClass) {
annotationParameterTypeDependencies.put(javaClass, annotation);
}
@Override
public void visitEnumConstant(String propertyName, JavaEnumConstant enumConstant) {
annotationParameterTypeDependencies.put(enumConstant.getDeclaringClass(), annotation);
}
@Override
public void visitAnnotation(String propertyName, JavaAnnotation> memberAnnotation) {
annotationParameterTypeDependencies.put(memberAnnotation.getRawType(), annotation);
memberAnnotation.accept(this);
}
});
}
}
private Set> findAnnotations(JavaClass clazz) {
Set> result = Sets.newHashSet(clazz.getAnnotations());
for (JavaMember member : clazz.getMembers()) {
result.addAll(member.getAnnotations());
}
return result;
}
private void registerStaticInitializer(JavaClass clazz) {
if (clazz.getStaticInitializer().isPresent()) {
for (InstanceofCheck instanceofCheck : clazz.getStaticInitializer().get().getInstanceofChecks()) {
instanceofCheckDependencies.put(instanceofCheck.getRawType(), instanceofCheck);
}
}
}
void finish(Iterable classes) {
ReverseDependencies reverseDependencies = new ReverseDependencies(this);
for (JavaClass clazz : classes) {
clazz.setReverseDependencies(reverseDependencies);
}
}
}
private static class ResolvingAccessLoader> extends CacheLoader> {
private final SetMultimap accessesToSelf;
private ResolvingAccessLoader(SetMultimap accessesToSelf) {
this.accessesToSelf = accessesToSelf;
}
@Override
public Set load(MEMBER member) {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaClass javaClass : getPossibleTargetClassesForAccess(member.getOwner())) {
for (ACCESS access : this.accessesToSelf.get(javaClass)) {
Optional extends JavaMember> target = access.getTarget().resolveMember();
if (target.isPresent() && target.get().equals(member)) {
result.add(access);
}
}
}
return result.build();
}
private Set getPossibleTargetClassesForAccess(JavaClass owner) {
return ImmutableSet.builder()
.add(owner)
.addAll(owner.getAllSubclasses())
.build();
}
}
private static class ConstructorAccessLoader> extends CacheLoader> {
private final SetMultimap accessesToSelf;
private ConstructorAccessLoader(SetMultimap accessesToSelf) {
this.accessesToSelf = accessesToSelf;
}
@Override
public Set load(JavaConstructor member) {
ImmutableSet.Builder result = ImmutableSet.builder();
result.addAll(accessesToSelf.get(member.getFullName()));
return result.build();
}
}
}