Skip to content

Commit

Permalink
add method JavaClass.getTransitiveDependenciesFromSelf
Browse files Browse the repository at this point in the history
Signed-off-by: Manfred Hanke <[email protected]>
  • Loading branch information
hankem committed Dec 15, 2020
1 parent 883ef99 commit e9ead20
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,17 @@ public Set<Dependency> getDirectDependenciesFromSelf() {
return javaClassDependencies.getDirectDependenciesFromClass();
}

/**
* Returns the transitive closure of all dependencies originating from this class, i.e. its direct dependencies
* and the dependencies from all imported target classes.
* @return all transitive dependencies (including direct dependencies) from this class
* @see #getDirectDependenciesFromSelf()
*/
@PublicAPI(usage = ACCESS)
public Set<Dependency> getTransitiveDependenciesFromSelf() {
return JavaClassTransitiveDependencies.findTransitiveDependenciesFrom(this);
}

/**
* Like {@link #getDirectDependenciesFromSelf()}, but instead returns all dependencies where this class
* is target.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2014-2020 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 com.google.common.collect.ImmutableSet;

import java.util.HashSet;
import java.util.Set;

class JavaClassTransitiveDependencies {
private JavaClassTransitiveDependencies() {
}

static Set<Dependency> findTransitiveDependenciesFrom(JavaClass javaClass) {
ImmutableSet.Builder<Dependency> transitiveDependencies = ImmutableSet.builder();
Set<JavaClass> analyzedClasses = new HashSet<>(); // to avoid infinite recursion for cyclic dependencies
addTransitiveDependenciesFrom(javaClass, transitiveDependencies, analyzedClasses);
return transitiveDependencies.build();
}

private static void addTransitiveDependenciesFrom(JavaClass javaClass, ImmutableSet.Builder<Dependency> transitiveDependencies, Set<JavaClass> analyzedClasses) {
analyzedClasses.add(javaClass); // currently being analyzed
Set<JavaClass> targetClassesToRecurse = new HashSet<>();
for (Dependency dependency : javaClass.getDirectDependenciesFromSelf()) {
transitiveDependencies.add(dependency);
targetClassesToRecurse.add(dependency.getTargetClass().getBaseComponentType());
}
for (JavaClass targetClass : targetClassesToRecurse) {
if (!analyzedClasses.contains(targetClass)) {
addTransitiveDependenciesFrom(targetClass, transitiveDependencies, analyzedClasses);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.tngtech.archunit.core.domain;

import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.junit.Test;

import static com.tngtech.archunit.testutil.Assertions.assertThatDependencies;

public class JavaClassTransitiveDependenciesTest {

static class AcyclicGraph {
static class A {
B b;
C[][] c;
}

static class B {
Integer i;
}

static class C {
D d;
}

static class D {
String s;
}
}

@Test
public void findsTransitiveDependenciesInAcyclicGraph() {
Class<?> a = AcyclicGraph.A.class;
Class<?> b = AcyclicGraph.B.class;
Class<?> c = AcyclicGraph.C.class;
Class<?> d = AcyclicGraph.D.class;
JavaClasses classes = new ClassFileImporter().importClasses(a, b, c, d);
Class<?> cArray = AcyclicGraph.C[][].class;

// @formatter:off
assertThatDependencies(classes.get(a).getTransitiveDependenciesFromSelf())
.contain(a, Object.class)
.contain(a, b)
.contain(b, Object.class)
.contain(b, Integer.class)
.contain(a, cArray)
.contain(c, Object.class)
.contain(c, d)
.contain(d, Object.class)
.contain(d, String.class);

assertThatDependencies(classes.get(b).getTransitiveDependenciesFromSelf())
.contain(b, Object.class)
.contain(b, Integer.class);

assertThatDependencies(classes.get(c).getTransitiveDependenciesFromSelf())
.contain(c, Object.class)
.contain(c, d)
.contain(d, Object.class)
.contain(d, String.class);
// @formatter:on
}


static class CyclicGraph {
static class A {
B b;
C[][] c;
D d;
}

static class B {
Integer i;
}

static class C {
A a;
}

static class D {
E e;
}

static class E {
A a;
String s;
}
}

@Test
public void findsTransitiveDependenciesInCyclicGraph() {
Class<?> a = CyclicGraph.A.class;
Class<?> b = CyclicGraph.B.class;
Class<?> c = CyclicGraph.C.class;
Class<?> d = CyclicGraph.D.class;
Class<?> e = CyclicGraph.E.class;
JavaClasses classes = new ClassFileImporter().importClasses(a, b, c, d, e);
Class<?> cArray = CyclicGraph.C[][].class;

// @formatter:off
assertThatDependencies(classes.get(a).getTransitiveDependenciesFromSelf())
.contain(a, Object.class)
.contain(a, b)
.contain(b, Object.class)
.contain(b, Integer.class)
.contain(a, cArray)
.contain(c, Object.class)
.contain(c, a)
.contain(a, d)
.contain(d, Object.class)
.contain(d, e)
.contain(e, Object.class)
.contain(e, a)
.contain(e, String.class);

assertThatDependencies(classes.get(c).getTransitiveDependenciesFromSelf())
.contain(c, Object.class)
.contain(c, a)
.contain(a, Object.class)
.contain(a, b)
.contain(b, Object.class)
.contain(b, Integer.class)
.contain(a, cArray)
.contain(a, d)
.contain(d, Object.class)
.contain(d, e)
.contain(e, Object.class)
.contain(e, a)
.contain(e, String.class);

assertThatDependencies(classes.get(d).getTransitiveDependenciesFromSelf())
.contain(d, Object.class)
.contain(d, e)
.contain(e, Object.class)
.contain(e, a)
.contain(a, Object.class)
.contain(a, b)
.contain(b, Object.class)
.contain(b, Integer.class)
.contain(a, cArray)
.contain(c, Object.class)
.contain(c, a)
.contain(a, d)
.contain(e, String.class);
// @formatter:on
}
}

0 comments on commit e9ead20

Please sign in to comment.