All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tngtech.archunit.core.domain.ReverseDependencies Maven / Gradle / Ivy

Go to download

A Java architecture test library, to specify and assert architecture rules in plain Java - Module 'archunit'

There is a newer version: 1.3.0
Show newest version
/*
 * 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 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();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy