Skip to content

Commit 7cb1b76

Browse files
committed
add method JavaClass.getTransitiveDependenciesFromSelf
Signed-off-by: Manfred Hanke <[email protected]>
1 parent 4786ed4 commit 7cb1b76

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,17 @@ public Set<Dependency> getDirectDependenciesFromSelf() {
10311031
return javaClassDependencies.getDirectDependenciesFromClass();
10321032
}
10331033

1034+
/**
1035+
* Returns the transitive closure of all dependencies originating from this class, i.e. its direct dependencies
1036+
* and the dependencies from all imported target classes.
1037+
* @return all transitive dependencies (including direct dependencies) from this class
1038+
* @see #getDirectDependenciesFromSelf()
1039+
*/
1040+
@PublicAPI(usage = ACCESS)
1041+
public Set<Dependency> getTransitiveDependenciesFromSelf() {
1042+
return JavaClassTransitiveDependencies.findTransitiveDependenciesFrom(this);
1043+
}
1044+
10341045
/**
10351046
* Like {@link #getDirectDependenciesFromSelf()}, but instead returns all dependencies where this class
10361047
* is target.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2014-2020 TNG Technology Consulting GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.tngtech.archunit.core.domain;
17+
18+
import com.google.common.collect.ImmutableSet;
19+
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
class JavaClassTransitiveDependencies {
24+
private JavaClassTransitiveDependencies() {
25+
}
26+
27+
static Set<Dependency> findTransitiveDependenciesFrom(JavaClass javaClass) {
28+
ImmutableSet.Builder<Dependency> transitiveDependencies = ImmutableSet.builder();
29+
Set<JavaClass> analyzedClasses = new HashSet<>(); // to avoid infinite recursion for cyclic dependencies
30+
addTransitiveDependenciesFrom(javaClass, transitiveDependencies, analyzedClasses);
31+
return transitiveDependencies.build();
32+
}
33+
34+
private static void addTransitiveDependenciesFrom(JavaClass javaClass, ImmutableSet.Builder<Dependency> transitiveDependencies, Set<JavaClass> analyzedClasses) {
35+
analyzedClasses.add(javaClass); // currently being analyzed
36+
Set<JavaClass> targetClassesToRecurse = new HashSet<>();
37+
for (Dependency dependency : javaClass.getDirectDependenciesFromSelf()) {
38+
transitiveDependencies.add(dependency);
39+
targetClassesToRecurse.add(dependency.getTargetClass().getBaseComponentType());
40+
}
41+
for (JavaClass targetClass : targetClassesToRecurse) {
42+
if (!analyzedClasses.contains(targetClass)) {
43+
addTransitiveDependenciesFrom(targetClass, transitiveDependencies, analyzedClasses);
44+
}
45+
}
46+
}
47+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.tngtech.archunit.core.domain;
2+
3+
import com.tngtech.archunit.core.importer.ClassFileImporter;
4+
import org.junit.Test;
5+
6+
import static com.tngtech.archunit.testutil.Assertions.assertThatDependencies;
7+
8+
public class JavaClassTransitiveDependenciesTest {
9+
10+
@SuppressWarnings("unused")
11+
static class AcyclicGraph {
12+
static class A {
13+
B b;
14+
C[][] c;
15+
}
16+
17+
static class B {
18+
Integer i;
19+
}
20+
21+
static class C {
22+
D d;
23+
}
24+
25+
static class D {
26+
String s;
27+
}
28+
}
29+
30+
@Test
31+
public void findsTransitiveDependenciesInAcyclicGraph() {
32+
Class<?> a = AcyclicGraph.A.class;
33+
Class<?> b = AcyclicGraph.B.class;
34+
Class<?> c = AcyclicGraph.C.class;
35+
Class<?> d = AcyclicGraph.D.class;
36+
JavaClasses classes = new ClassFileImporter().importClasses(a, b, c, d);
37+
Class<?> cArray = AcyclicGraph.C[][].class;
38+
39+
// @formatter:off
40+
assertThatDependencies(classes.get(a).getTransitiveDependenciesFromSelf())
41+
.contain(a, Object.class)
42+
.contain(a, b)
43+
.contain(b, Object.class)
44+
.contain(b, Integer.class)
45+
.contain(a, cArray)
46+
.contain(c, Object.class)
47+
.contain(c, d)
48+
.contain(d, Object.class)
49+
.contain(d, String.class);
50+
51+
assertThatDependencies(classes.get(b).getTransitiveDependenciesFromSelf())
52+
.contain(b, Object.class)
53+
.contain(b, Integer.class);
54+
55+
assertThatDependencies(classes.get(c).getTransitiveDependenciesFromSelf())
56+
.contain(c, Object.class)
57+
.contain(c, d)
58+
.contain(d, Object.class)
59+
.contain(d, String.class);
60+
// @formatter:on
61+
}
62+
63+
@SuppressWarnings("unused")
64+
static class CyclicGraph {
65+
static class A {
66+
B b;
67+
C[][] c;
68+
D d;
69+
}
70+
71+
static class B {
72+
Integer i;
73+
}
74+
75+
static class C {
76+
A a;
77+
}
78+
79+
static class D {
80+
E e;
81+
}
82+
83+
static class E {
84+
A a;
85+
String s;
86+
}
87+
}
88+
89+
@Test
90+
public void findsTransitiveDependenciesInCyclicGraph() {
91+
Class<?> a = CyclicGraph.A.class;
92+
Class<?> b = CyclicGraph.B.class;
93+
Class<?> c = CyclicGraph.C.class;
94+
Class<?> d = CyclicGraph.D.class;
95+
Class<?> e = CyclicGraph.E.class;
96+
JavaClasses classes = new ClassFileImporter().importClasses(a, b, c, d, e);
97+
Class<?> cArray = CyclicGraph.C[][].class;
98+
99+
// @formatter:off
100+
assertThatDependencies(classes.get(a).getTransitiveDependenciesFromSelf())
101+
.contain(a, Object.class)
102+
.contain(a, b)
103+
.contain(b, Object.class)
104+
.contain(b, Integer.class)
105+
.contain(a, cArray)
106+
.contain(c, Object.class)
107+
.contain(c, a)
108+
.contain(a, d)
109+
.contain(d, Object.class)
110+
.contain(d, e)
111+
.contain(e, Object.class)
112+
.contain(e, a)
113+
.contain(e, String.class);
114+
115+
assertThatDependencies(classes.get(c).getTransitiveDependenciesFromSelf())
116+
.contain(c, Object.class)
117+
.contain(c, a)
118+
.contain(a, Object.class)
119+
.contain(a, b)
120+
.contain(b, Object.class)
121+
.contain(b, Integer.class)
122+
.contain(a, cArray)
123+
.contain(a, d)
124+
.contain(d, Object.class)
125+
.contain(d, e)
126+
.contain(e, Object.class)
127+
.contain(e, a)
128+
.contain(e, String.class);
129+
130+
assertThatDependencies(classes.get(d).getTransitiveDependenciesFromSelf())
131+
.contain(d, Object.class)
132+
.contain(d, e)
133+
.contain(e, Object.class)
134+
.contain(e, a)
135+
.contain(a, Object.class)
136+
.contain(a, b)
137+
.contain(b, Object.class)
138+
.contain(b, Integer.class)
139+
.contain(a, cArray)
140+
.contain(c, Object.class)
141+
.contain(c, a)
142+
.contain(a, d)
143+
.contain(e, String.class);
144+
// @formatter:on
145+
}
146+
}

0 commit comments

Comments
 (0)