Static data-flow and variable lineage mapping for Java. The tool parses source, extracts how values are declared, copied, merged, and mutated, and renders an interactive graph in the browser. The aim is to cut through syntax and visualize variable intent.
The app is split into a headless Java analyzer and a lightweight web visualizer.
- Parser: JavaParser with JavaSymbolSolver for type/scope resolution
- Server: Javalin
- Responsibilities:
- Accepts a source input (single file, source root, or .zip of sources)
- Builds an AST, extracts variable/field relationships
- Produces a JSON data-flow graph
- Tech: Vanilla HTML/JS served as static assets
- Responsibilities:
- Calls the /analyze endpoint
- Renders a directed node graph with basic interactions and a node types legend on the Graph view
This project uses Maven.
mvn clean compileStart the analyzer server (defaults to port 7000):
mvn exec:java -Dexec.mainClass="io.javavisualvariable.Main"Open the UI:
Provide query parameters in the page URL; the UI forwards them to /analyze.
Accepts exactly one of the following query parameters. If multiple are provided, precedence is: file, then sourcePath, then sourceZip.
-
file
- What: Relative path under src/main/resources to a single .java file.
- Example values: Example.java, samples/MyClass.java
- Example requests:
- Browser UI: http://localhost:7000/?file=Example.java
- Direct: curl "http://localhost:7000/analyze?file=Example.java"
-
sourcePath
- What: Path to a directory treated as a source root. All .java files are analyzed.
- Notes: Can be absolute or relative to the server working directory.
- Example requests:
-
sourceZip
- What: Path to a .zip archive containing Java sources to analyze.
- Example requests:
Responses are application/json. On error, the server responds with a non-200 status and a short JSON error, e.g. {"error":"..."}.
The server returns a GraphPayload object with the following structure (see src/main/java/io/javavisual/GraphModels.java):
-
GraphPayload
- nodes: Node[]
- edges: Edge[]
-
Node
- id: string — scope-qualified, globally unique identifier
- label: string — human-readable name for display (variable/field)
- type: NodeType — one of:
- PARAM
- COPY
- MERGE
- OBJECT_CREATE
- CALL
- FIELD_REF
- FIELD
- OTHER
- EXTERNAL
- line: number — source line number
- op: string (optional) — operator, method name, constructed type name, etc.
-
Edge
- source: string — source node id
- target: string — target node id
Example:
{
"nodes": [
{ "id": "MyClass#myMethod:param:a:10", "label": "a", "type": "PARAM", "line": 10 },
{ "id": "MyClass#myMethod:local:b:12", "label": "b", "type": "COPY", "line": 12 },
{ "id": "MyClass#myMethod:local:c:15", "label": "c", "type": "MERGE", "line": 15, "op": "+" },
{ "id": "MyClass#myMethod:local:t:20", "label": "t", "type": "OBJECT_CREATE", "line": 20, "op": "new StringBuilder" }
],
"edges": [
{ "source": "MyClass#myMethod:param:a:10", "target": "MyClass#myMethod:local:b:12" },
{ "source": "MyClass#myMethod:param:a:10", "target": "MyClass#myMethod:local:c:15" },
{ "source": "MyClass#myMethod:local:b:12", "target": "MyClass#myMethod:local:c:15" }
]
}Notes:
- Node.id is scope-qualified to avoid collisions across methods/classes.
- Edges are unique on (source,target) and may be de-duplicated server-side.
This project is licensed under the MIT License.
The frontend vendors two third-party libraries (@antv/x6 and dagre) under
src/main/resources/public/vendor/; both are also MIT-licensed. See
THIRD_PARTY_NOTICES.md for their copyright/license
text.
