-
Notifications
You must be signed in to change notification settings - Fork 0
/
ClassycleDependencyUtils.java
126 lines (116 loc) · 5.02 KB
/
ClassycleDependencyUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package ba.sake.hepek.core;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import classycle.Analyser;
import classycle.graph.AtomicVertex;
/**
* Handy util methods for getting static reverse dependencies of classes. <br>
* Uses Classycle Analyser internally. <br>
* Contains only static methods.
*
* @author Sake
*/
public class ClassycleDependencyUtils {
private ClassycleDependencyUtils() {
throw new UnsupportedOperationException("Can't instantiate ClassycleDependencyUtils.");
}
/* DIRECT REVERSE DEPENDENCIES, simple */
/**
* @param classVertices
* Classes to analyse.
* @return Direct reverse dependencies of classes.
*/
static Map<AtomicVertex, Set<AtomicVertex>> getDirectRevDeps(final AtomicVertex[] classVertices) {
final Map<AtomicVertex, Set<AtomicVertex>> directRevDeps = new HashMap<>(classVertices.length);
for (final AtomicVertex classVertex : classVertices) {
final int reverseDepsCount = classVertex.getNumberOfIncomingArcs();
final Set<AtomicVertex> revDepsOfClass = new HashSet<>(reverseDepsCount);
for (int i = 0; i < reverseDepsCount; i++) {
final AtomicVertex revDep = (AtomicVertex) classVertex.getTailVertex(i);
revDepsOfClass.add(revDep);
}
directRevDeps.put(classVertex, revDepsOfClass);
}
return directRevDeps;
}
/* TRANSITIVE REVERSE DEPENDENCIES, semi-simple */
/**
* @param classVertices
* Classes to analyse.
* @return Transitive reverse dependencies of classes. <br>
* Transitive means (direct deps) + (deps of deps) + (deps of deps of deps) + etc.
*/
static Map<AtomicVertex, Set<AtomicVertex>> getTransitiveRevDeps(final AtomicVertex[] classVertices) {
final Map<AtomicVertex, Set<AtomicVertex>> directDeps = getDirectRevDeps(classVertices);
final Map<AtomicVertex, Set<AtomicVertex>> result = new HashMap<>(directDeps.keySet().size());
for (final AtomicVertex v : directDeps.keySet()) {
final Set<AtomicVertex> transitiveDeps = getTransitiveRevDepsForClass(v, directDeps, new HashSet<>());
result.put(v, transitiveDeps);
}
return result;
}
/**
* Gets all dependencies of one class.
*
* @param className
* Class name for which we find rev-deps.
* @param deps
* Direct dependencies of all classes. See method `getDirectRevDeps`.
* @param visited
* Names of classes which are already traversed. (There could be cycles, that's why)
* @return All rev-deps of class called `className`.
*/
static Set<AtomicVertex> getTransitiveRevDepsForClass(final AtomicVertex classVertice,
final Map<AtomicVertex, Set<AtomicVertex>> directDeps, final Set<AtomicVertex> visited) {
// direct deps of given class
final Set<AtomicVertex> classDeps = directDeps.get(classVertice);
final Set<AtomicVertex> oldVisited = new HashSet<>(visited);
visited.addAll(classDeps);
// new deps to visited recursively
final Set<AtomicVertex> newTransitiveDeps = new HashSet<>();
for (final AtomicVertex v : classDeps) {
if (!oldVisited.contains(v)) {
final Set<AtomicVertex> transitiveDeps = getTransitiveRevDepsForClass(v, directDeps, visited);
newTransitiveDeps.addAll(transitiveDeps);
}
}
classDeps.addAll(newTransitiveDeps); // collect all results
// must use Iterator for removal, bcoz ConcurrentModificationException
for (final Iterator<AtomicVertex> iterator = classDeps.iterator(); iterator.hasNext();) {
if (iterator.next().equals(classVertice)) { // class can't depend on itself, LOL..
iterator.remove();
}
}
return classDeps;
}
/**
* Calculates static reverse dependencies of classes.
*
* @param files
* Files/Directories to process.
* @param directOnly
* If true, direct reverse dependencies are calculated for each class. If false, transitive reverse
* dependencies are calculated for each class.
* @return Reverse dependencies of classes.
*/
public static Map<AtomicVertex, Set<AtomicVertex>> reverseDependencies(final List<File> files,
final boolean directOnly) {
final String[] filePaths = new String[files.size()];
for (int i = 0; i < files.size(); i++) {
final File f = files.get(i);
filePaths[i] = f.getAbsolutePath();
}
final Analyser analyser = new Analyser(filePaths);
final AtomicVertex[] classVertices = analyser.getClassGraph();
if (directOnly) {
return getDirectRevDeps(classVertices);
} else {
return getTransitiveRevDeps(classVertices);
}
}
}