Skip to content

Commit e9ead20

Browse files
committed
add method JavaClass.getTransitiveDependenciesFromSelf
Signed-off-by: Manfred Hanke <[email protected]>
1 parent 883ef99 commit e9ead20

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-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: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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+
static class AcyclicGraph {
11+
static class A {
12+
B b;
13+
C[][] c;
14+
}
15+
16+
static class B {
17+
Integer i;
18+
}
19+
20+
static class C {
21+
D d;
22+
}
23+
24+
static class D {
25+
String s;
26+
}
27+
}
28+
29+
@Test
30+
public void findsTransitiveDependenciesInAcyclicGraph() {
31+
Class<?> a = AcyclicGraph.A.class;
32+
Class<?> b = AcyclicGraph.B.class;
33+
Class<?> c = AcyclicGraph.C.class;
34+
Class<?> d = AcyclicGraph.D.class;
35+
JavaClasses classes = new ClassFileImporter().importClasses(a, b, c, d);
36+
Class<?> cArray = AcyclicGraph.C[][].class;
37+
38+
// @formatter:off
39+
assertThatDependencies(classes.get(a).getTransitiveDependenciesFromSelf())
40+
.contain(a, Object.class)
41+
.contain(a, b)
42+
.contain(b, Object.class)
43+
.contain(b, Integer.class)
44+
.contain(a, cArray)
45+
.contain(c, Object.class)
46+
.contain(c, d)
47+
.contain(d, Object.class)
48+
.contain(d, String.class);
49+
50+
assertThatDependencies(classes.get(b).getTransitiveDependenciesFromSelf())
51+
.contain(b, Object.class)
52+
.contain(b, Integer.class);
53+
54+
assertThatDependencies(classes.get(c).getTransitiveDependenciesFromSelf())
55+
.contain(c, Object.class)
56+
.contain(c, d)
57+
.contain(d, Object.class)
58+
.contain(d, String.class);
59+
// @formatter:on
60+
}
61+
62+
63+
static class CyclicGraph {
64+
static class A {
65+
B b;
66+
C[][] c;
67+
D d;
68+
}
69+
70+
static class B {
71+
Integer i;
72+
}
73+
74+
static class C {
75+
A a;
76+
}
77+
78+
static class D {
79+
E e;
80+
}
81+
82+
static class E {
83+
A a;
84+
String s;
85+
}
86+
}
87+
88+
@Test
89+
public void findsTransitiveDependenciesInCyclicGraph() {
90+
Class<?> a = CyclicGraph.A.class;
91+
Class<?> b = CyclicGraph.B.class;
92+
Class<?> c = CyclicGraph.C.class;
93+
Class<?> d = CyclicGraph.D.class;
94+
Class<?> e = CyclicGraph.E.class;
95+
JavaClasses classes = new ClassFileImporter().importClasses(a, b, c, d, e);
96+
Class<?> cArray = CyclicGraph.C[][].class;
97+
98+
// @formatter:off
99+
assertThatDependencies(classes.get(a).getTransitiveDependenciesFromSelf())
100+
.contain(a, Object.class)
101+
.contain(a, b)
102+
.contain(b, Object.class)
103+
.contain(b, Integer.class)
104+
.contain(a, cArray)
105+
.contain(c, Object.class)
106+
.contain(c, a)
107+
.contain(a, d)
108+
.contain(d, Object.class)
109+
.contain(d, e)
110+
.contain(e, Object.class)
111+
.contain(e, a)
112+
.contain(e, String.class);
113+
114+
assertThatDependencies(classes.get(c).getTransitiveDependenciesFromSelf())
115+
.contain(c, Object.class)
116+
.contain(c, a)
117+
.contain(a, Object.class)
118+
.contain(a, b)
119+
.contain(b, Object.class)
120+
.contain(b, Integer.class)
121+
.contain(a, cArray)
122+
.contain(a, d)
123+
.contain(d, Object.class)
124+
.contain(d, e)
125+
.contain(e, Object.class)
126+
.contain(e, a)
127+
.contain(e, String.class);
128+
129+
assertThatDependencies(classes.get(d).getTransitiveDependenciesFromSelf())
130+
.contain(d, Object.class)
131+
.contain(d, e)
132+
.contain(e, Object.class)
133+
.contain(e, a)
134+
.contain(a, Object.class)
135+
.contain(a, b)
136+
.contain(b, Object.class)
137+
.contain(b, Integer.class)
138+
.contain(a, cArray)
139+
.contain(c, Object.class)
140+
.contain(c, a)
141+
.contain(a, d)
142+
.contain(e, String.class);
143+
// @formatter:on
144+
}
145+
}

0 commit comments

Comments
 (0)