Skip to content

Commit 2e476ed

Browse files
Capturing parent namespace and location
Capturing parent namespace and location relative to repo root for classes and packages
1 parent d798d64 commit 2e476ed

49 files changed

Lines changed: 2120 additions & 921 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎codebase-graph-builder/src/main/java/org/hjug/graphbuilder/CodebaseGraphDTO.java‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
@ToString
2323
public class CodebaseGraphDTO {
2424

25-
private final Graph<String, DefaultWeightedEdge> classReferencesGraph;
26-
private final Graph<String, DefaultWeightedEdge> packageReferencesGraph;
25+
private final Graph<VertexInfo, DefaultWeightedEdge> classReferencesGraph;
26+
private final Graph<VertexInfo, DefaultWeightedEdge> packageReferencesGraph;
2727
private final Map<DefaultWeightedEdge, Set<DefaultWeightedEdge>> classRelationshipsInPackageRelationship;
2828
// used for looking up files where classes reside
2929
private final Map<String, String> classToSourceFilePathMapping;
@@ -35,15 +35,17 @@ public class CodebaseGraphDTO {
3535
* Creates a new CodebaseGraphDTO.
3636
*
3737
* @param classReferencesGraph the class reference graph
38+
* (vertices are {@link VertexInfo})
3839
* @param packageReferencesGraph the package reference graph
40+
* (vertices are {@link VertexInfo})
3941
* @param classRelationshipsInPackageRelationship the class-to-package relationships
4042
* @param classToSourceFilePathMapping the class to source file path mapping
4143
* @param classDisharmonies the list of class disharmonies
4244
* @param methodDisharmonies the list of method disharmonies
4345
*/
4446
public CodebaseGraphDTO(
45-
Graph<String, DefaultWeightedEdge> classReferencesGraph,
46-
Graph<String, DefaultWeightedEdge> packageReferencesGraph,
47+
Graph<VertexInfo, DefaultWeightedEdge> classReferencesGraph,
48+
Graph<VertexInfo, DefaultWeightedEdge> packageReferencesGraph,
4749
Map<DefaultWeightedEdge, Set<DefaultWeightedEdge>> classRelationshipsInPackageRelationship,
4850
Map<String, String> classToSourceFilePathMapping,
4951
List<ClassDisharmony> classDisharmonies,

‎codebase-graph-builder/src/main/java/org/hjug/graphbuilder/CompositeGraphBuilder.java‎

Lines changed: 79 additions & 63 deletions
Large diffs are not rendered by default.

‎codebase-graph-builder/src/main/java/org/hjug/graphbuilder/GraphDependencyCollector.java‎

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
public class GraphDependencyCollector implements DependencyCollector {
1414

1515
@Getter
16-
private final Graph<String, DefaultWeightedEdge> classReferencesGraph;
16+
private final Graph<VertexInfo, DefaultWeightedEdge> classReferencesGraph;
1717

1818
@Getter
19-
private final Graph<String, DefaultWeightedEdge> packageReferencesGraph;
19+
private final Graph<VertexInfo, DefaultWeightedEdge> packageReferencesGraph;
2020

2121
@Getter
2222
private final Set<String> packagesInCodebase = new HashSet<>();
@@ -25,9 +25,22 @@ public class GraphDependencyCollector implements DependencyCollector {
2525
private final Map<DefaultWeightedEdge, Set<DefaultWeightedEdge>> classRelationshipsInPackageRelationship =
2626
new HashMap<>();
2727

28+
/**
29+
* Class locations (as reported by the visitors) captured during collection,
30+
* keyed by class FQN. The repository-relative locations used for rendering
31+
* are hydrated onto the {@link VertexInfo} vertices from the canonical
32+
* class-to-source-file mapping after the walk (see {@link VertexInfoHydrator});
33+
* this map keeps the raw information captured at collection time.
34+
*/
35+
@Getter
36+
private final Map<String, String> recordedClassLocations = new HashMap<>();
37+
38+
/** Registry of the VertexInfo instances created for each vertex name. */
39+
private final Map<String, VertexInfo> vertexRegistry = new HashMap<>();
40+
2841
public GraphDependencyCollector(
29-
Graph<String, DefaultWeightedEdge> classReferencesGraph,
30-
Graph<String, DefaultWeightedEdge> packageReferencesGraph) {
42+
Graph<VertexInfo, DefaultWeightedEdge> classReferencesGraph,
43+
Graph<VertexInfo, DefaultWeightedEdge> packageReferencesGraph) {
3144
this.classReferencesGraph = classReferencesGraph;
3245
this.packageReferencesGraph = packageReferencesGraph;
3346
}
@@ -38,14 +51,16 @@ public void addClassDependency(String fromClassFqn, String toClassFqn) {
3851
return;
3952
}
4053

41-
classReferencesGraph.addVertex(fromClassFqn);
42-
classReferencesGraph.addVertex(toClassFqn);
54+
VertexInfo fromVertex = getOrCreateVertex(fromClassFqn);
55+
VertexInfo toVertex = getOrCreateVertex(toClassFqn);
56+
classReferencesGraph.addVertex(fromVertex);
57+
classReferencesGraph.addVertex(toVertex);
4358

4459
DefaultWeightedEdge classRelationship;
45-
if (!classReferencesGraph.containsEdge(fromClassFqn, toClassFqn)) {
46-
classRelationship = classReferencesGraph.addEdge(fromClassFqn, toClassFqn);
60+
if (!classReferencesGraph.containsEdge(fromVertex, toVertex)) {
61+
classRelationship = classReferencesGraph.addEdge(fromVertex, toVertex);
4762
} else {
48-
classRelationship = classReferencesGraph.getEdge(fromClassFqn, toClassFqn);
63+
classRelationship = classReferencesGraph.getEdge(fromVertex, toVertex);
4964
classReferencesGraph.setEdgeWeight(
5065
classRelationship, classReferencesGraph.getEdgeWeight(classRelationship) + 1);
5166
}
@@ -80,14 +95,16 @@ public DefaultWeightedEdge addPackageDependency(String fromClassFqn, String toCl
8095
return null;
8196
}
8297

83-
packageReferencesGraph.addVertex(fromPackageName);
84-
packageReferencesGraph.addVertex(toPackageName);
98+
VertexInfo fromPackageVertex = getOrCreateVertex(fromPackageName);
99+
VertexInfo toPackageVertex = getOrCreateVertex(toPackageName);
100+
packageReferencesGraph.addVertex(fromPackageVertex);
101+
packageReferencesGraph.addVertex(toPackageVertex);
85102

86103
DefaultWeightedEdge packageEdge;
87-
if (!packageReferencesGraph.containsEdge(fromPackageName, toPackageName)) {
88-
packageEdge = packageReferencesGraph.addEdge(fromPackageName, toPackageName);
104+
if (!packageReferencesGraph.containsEdge(fromPackageVertex, toPackageVertex)) {
105+
packageEdge = packageReferencesGraph.addEdge(fromPackageVertex, toPackageVertex);
89106
} else {
90-
packageEdge = packageReferencesGraph.getEdge(fromPackageName, toPackageName);
107+
packageEdge = packageReferencesGraph.getEdge(fromPackageVertex, toPackageVertex);
91108
packageReferencesGraph.setEdgeWeight(packageEdge, packageReferencesGraph.getEdgeWeight(packageEdge) + 1);
92109
}
93110

@@ -102,9 +119,20 @@ protected String getPackageFromFqn(String fqn) {
102119
return fqn.substring(0, lastIndex);
103120
}
104121

122+
/**
123+
* Returns the registered {@link VertexInfo} for the given class or package
124+
* name, creating and registering one (with the parent namespace derived
125+
* from the name) when absent.
126+
*/
127+
private VertexInfo getOrCreateVertex(String name) {
128+
return vertexRegistry.computeIfAbsent(name, VertexInfo::forName);
129+
}
130+
105131
@Override
106132
public void recordClassLocation(String classFqn, String sourceFilePath) {
107-
// This will be handled by JavaVisitor which maintains the mapping
133+
// Capture the location reported during collection; repository-relative
134+
// hydration of the VertexInfo vertices happens after the walk.
135+
recordedClassLocations.put(classFqn, sourceFilePath);
108136
}
109137

110138
@Override
@@ -114,6 +142,6 @@ public void registerPackage(String packageName) {
114142

115143
@Override
116144
public void registerClassVertex(String classFqn) {
117-
classReferencesGraph.addVertex(classFqn);
145+
classReferencesGraph.addVertex(getOrCreateVertex(classFqn));
118146
}
119147
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package org.hjug.graphbuilder;
2+
3+
/**
4+
* Rich graph vertex captured when graph dependency information is collected.
5+
*
6+
* <p>Replaces the bare class/package name String that previously served as the
7+
* graph vertex so the renderer can build a composed codebase map from the
8+
* graph image. Carries:
9+
*
10+
* <ul>
11+
* <li>{@link #getName()} — the vertex name: the fully qualified class name
12+
* (class graph) or the package name (package graph).</li>
13+
* <li>{@link #getLocationOnDisk()} — the location on disk of the vertex
14+
* relative to the repository root (source file path for a class;
15+
* member-file directory for a package). Populated by hydration after the
16+
* visitor walk; may be {@code null} when no location is known.</li>
17+
* <li>{@link #getParentNamespace()} — the parent namespace: the parent
18+
* package if this vertex is a package, or the enclosing construct
19+
* (package or class) if the vertex is not a package and contains code.
20+
* Derived from the name at creation time.</li>
21+
* </ul>
22+
*
23+
* <p>Identity is {@code name}-based ({@code equals}/{@code hashCode}) so
24+
* JGraphT vertex deduplication behaves exactly like the previous String
25+
* vertices and code can probe graphs with cheap {@link #forName(String)}
26+
* lookups. {@code toString()} returns the name so edge rendering
27+
* ({@code "(source : target)"}) is unchanged.
28+
*/
29+
public class VertexInfo {
30+
31+
private final String name;
32+
private final String parentNamespace;
33+
private String locationOnDisk;
34+
35+
private VertexInfo(String name, String parentNamespace) {
36+
this.name = name;
37+
this.parentNamespace = parentNamespace;
38+
}
39+
40+
/**
41+
* Creates a vertex for the given class or package name with the parent
42+
* namespace derived from the name.
43+
*
44+
* @param name the class FQN or package name; must not be null or empty
45+
* @return a VertexInfo whose parent namespace is the enclosing package
46+
* (for a top-level class or a package) or the enclosing class (for
47+
* a nested, anonymous, or synthetic class); {@code null} when no
48+
* enclosing construct can be derived
49+
* @throws NullPointerException if {@code name} is null
50+
* @throws IllegalArgumentException if {@code name} is empty
51+
*/
52+
public static VertexInfo forName(String name) {
53+
if (name == null) {
54+
throw new NullPointerException("name must not be null");
55+
}
56+
if (name.isEmpty()) {
57+
throw new IllegalArgumentException("name must not be empty");
58+
}
59+
return new VertexInfo(name, deriveParentNamespace(name));
60+
}
61+
62+
/**
63+
* Derives the enclosing construct of a vertex from its name.
64+
*
65+
* <p>Rules:
66+
* <ul>
67+
* <li>Kotlin anonymous vertex ({@code pkg.Outer.<anonymous>}, trailing
68+
* segment starts with {@code <}): the text before the last dot; a
69+
* packageless literal {@code <anonymous>} has no parent.</li>
70+
* <li>Nested/anonymous/synthetic class ({@code $} in the simple name):
71+
* the enclosing class (text before the last {@code $}).</li>
72+
* <li>Top-level class or package: the text before the last dot (the
73+
* containing package / parent package); {@code null} when the name
74+
* has no dot (default package).</li>
75+
* </ul>
76+
*
77+
* @param name the class FQN or package name
78+
* @return the parent namespace, or {@code null} when none can be derived
79+
*/
80+
static String deriveParentNamespace(String name) {
81+
int lastDot = name.lastIndexOf('.');
82+
String simpleName = lastDot >= 0 ? name.substring(lastDot + 1) : name;
83+
84+
if (simpleName.startsWith("<")) {
85+
// Kotlin anonymous: "pkg.Outer.<anonymous>" -> "pkg.Outer";
86+
// packageless "<anonymous>" has no enclosing construct
87+
return lastDot > 0 ? name.substring(0, lastDot) : null;
88+
}
89+
90+
int lastDollar = simpleName.lastIndexOf('$');
91+
if (lastDollar >= 0) {
92+
// nested / anonymous / synthetic: enclosing class before the last $
93+
String enclosingSimple = simpleName.substring(0, lastDollar);
94+
if (enclosingSimple.isEmpty()) {
95+
return null;
96+
}
97+
return lastDot >= 0 ? name.substring(0, lastDot + 1) + enclosingSimple : enclosingSimple;
98+
}
99+
100+
// top-level class or package: containing / parent package
101+
return lastDot >= 0 ? name.substring(0, lastDot) : null;
102+
}
103+
104+
/** Returns the vertex name (class FQN or package name). */
105+
public String getName() {
106+
return name;
107+
}
108+
109+
/**
110+
* Returns the location on disk of this vertex relative to the repository
111+
* root, or {@code null} when not (yet) known.
112+
*/
113+
public String getLocationOnDisk() {
114+
return locationOnDisk;
115+
}
116+
117+
/** Sets the repository-relative location on disk (used during hydration). */
118+
void setLocationOnDisk(String locationOnDisk) {
119+
this.locationOnDisk = locationOnDisk;
120+
}
121+
122+
/**
123+
* Returns the parent namespace: the parent package if this vertex is a
124+
* package, or the enclosing construct (package or class) if the vertex is
125+
* not a package and contains code; {@code null} when none can be derived.
126+
*/
127+
public String getParentNamespace() {
128+
return parentNamespace;
129+
}
130+
131+
@Override
132+
public boolean equals(Object o) {
133+
if (this == o) {
134+
return true;
135+
}
136+
if (!(o instanceof VertexInfo)) {
137+
return false;
138+
}
139+
return name.equals(((VertexInfo) o).name);
140+
}
141+
142+
@Override
143+
public int hashCode() {
144+
return name.hashCode();
145+
}
146+
147+
@Override
148+
public String toString() {
149+
return name;
150+
}
151+
}

0 commit comments

Comments
 (0)