Skip to content
Snippets Groups Projects
Commit ea93878c authored by Marcel Steinbeck's avatar Marcel Steinbeck
Browse files

Spaces -> tabs.

parent 6e95f8e8
No related branches found
No related tags found
No related merge requests found
///////////////////////////// Dependencies
@Grapes([
@Grab(group='com.github.javaparser', module='javaparser-core', version='3.6.7')]
@Grab(group='com.github.javaparser', module='javaparser-core', version='3.6.7')]
)
import com.github.javaparser.JavaParser
import groovy.io.FileType
......@@ -29,35 +29,35 @@ import static java.lang.System.exit
def cli = new CliBuilder(usage: 'dependencies.groovy [OPTIONS] DIRECTORY')
cli.header = 'Parses the given directory and generates a dependency graph. The resulting HTML code is printed to stdout.'
cli.with {
h longOpt: 'help',
'Show usage information.'
t longOpt: 'tension', args: 1, argName: 'tension',
"Set tension of edges (0.0 <= t <= 1.0).\ndefault: ${DEFAULT_TENSION}"
w longOpt: 'width', args: 1, argName: 'width',
"Set width of graphic (w >= 100).\ndefault: ${DEFAULT_WIDTH}"
p longOpt: 'padding', args: 1, argName: 'padding',
"Set padding of labels (p >= 150).\ndefault: ${DEFAULT_PADDING}"
x longOpt: 'exclude', args: 1, argName: '"regex"',
'Exclude qualified names matching "regex".'
h longOpt: 'help',
'Show usage information.'
t longOpt: 'tension', args: 1, argName: 'tension',
"Set tension of edges (0.0 <= t <= 1.0).\ndefault: ${DEFAULT_TENSION}"
w longOpt: 'width', args: 1, argName: 'width',
"Set width of graphic (w >= 100).\ndefault: ${DEFAULT_WIDTH}"
p longOpt: 'padding', args: 1, argName: 'padding',
"Set padding of labels (p >= 150).\ndefault: ${DEFAULT_PADDING}"
x longOpt: 'exclude', args: 1, argName: '"regex"',
'Exclude qualified names matching "regex".'
}
cli.footer = 'example: dependencies.groovy -x "org.junit.*" -t 0.9 -p 300 -w 1200 <PATH>'
def options = cli.parse(args)
if (!options) {
exit(1)
exit(1)
} else if (options.h) {
cli.usage()
exit(0)
cli.usage()
exit(0)
} else if (options.arguments().isEmpty()) {
println "error: Missing input directory"
cli.usage()
exit(1)
println "error: Missing input directory"
cli.usage()
exit(1)
} else if (!Files.exists(Paths.get(options.arguments().last()))) {
println "'${options.arguments().last()}' does not exist"
exit(1)
println "'${options.arguments().last()}' does not exist"
exit(1)
} else if (!Files.isDirectory(Paths.get(options.arguments().last()))) {
println "'${options.arguments().last()}' is not a directory"
exit(1)
println "'${options.arguments().last()}' is not a directory"
exit(1)
}
input = options.arguments().last()
......@@ -76,30 +76,30 @@ padding = Math.max(padding as int, 150)
///////////////////////////// Generate JSON string
Optional<String> parseJavaFile(Path p) {
String ex = exclude
def cu = JavaParser.parse(p)
def pkg = cu.packageDeclaration
.map{pd -> pd.nameAsString}
.orElse('')
def base = p.fileName.toString().replaceFirst('[.][^.]+$', '')
def name = "${pkg}.${base}"
if (name.matches(ex)) {
return Optional.empty()
}
def imports = String.join(',',
cu.imports.stream()
.map{i -> i.nameAsString}
.filter{i -> !i.matches(ex)}
.map{i -> "\"${i}\""}
.collect(Collectors.toList()))
return Optional.of('{"name":"'+name+'","size":1,"imports":['+imports+']}')
String ex = exclude
def cu = JavaParser.parse(p)
def pkg = cu.packageDeclaration
.map{pd -> pd.nameAsString}
.orElse('')
def base = p.fileName.toString().replaceFirst('[.][^.]+$', '')
def name = "${pkg}.${base}"
if (name.matches(ex)) {
return Optional.empty()
}
def imports = String.join(',',
cu.imports.stream()
.map{i -> i.nameAsString}
.filter{i -> !i.matches(ex)}
.map{i -> "\"${i}\""}
.collect(Collectors.toList()))
return Optional.of('{"name":"'+name+'","size":1,"imports":['+imports+']}')
}
def entries = []
new File(input).eachFileRecurse(FileType.FILES) {
if (it.name.endsWith(".java")) {
parseJavaFile(it.toPath()).ifPresent({ e -> entries << e })
}
if (it.name.endsWith(".java")) {
parseJavaFile(it.toPath()).ifPresent({ e -> entries << e })
}
}
def json = '[' + String.join(',', entries) + ']'
......@@ -107,14 +107,14 @@ def json = '[' + String.join(',', entries) + ']'
///////////////////////////// Generate HTML output
def d3 = new String(Files.readAllBytes(
Paths.get(SCRIPT_LOCATION, D3_SCRIPT_FILE)),
StandardCharsets.UTF_8)
Paths.get(SCRIPT_LOCATION, D3_SCRIPT_FILE)),
StandardCharsets.UTF_8)
def html = new String(Files.readAllBytes(
Paths.get(SCRIPT_LOCATION, HTML_IN_FILE)),
StandardCharsets.US_ASCII)
.replace('@JSON_STRING@', json)
.replace('@TENSION@', "${tension}")
.replace('@WIDTH@', "${width}")
.replace('@PADDING@', "${padding}")
.replace('@D3_SCRIPT@', d3)
Paths.get(SCRIPT_LOCATION, HTML_IN_FILE)),
StandardCharsets.US_ASCII)
.replace('@JSON_STRING@', json)
.replace('@TENSION@', "${tension}")
.replace('@WIDTH@', "${width}")
.replace('@PADDING@', "${padding}")
.replace('@D3_SCRIPT@', d3)
print html
......@@ -3,47 +3,47 @@
<style>
.node {
font: 300 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
fill: #bbb;
font: 300 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
fill: #bbb;
}
.node:hover {
fill: #000;
fill: #000;
}
.link {
stroke: steelblue;
stroke-opacity: 0.4;
fill: none;
pointer-events: none;
stroke: steelblue;
stroke-opacity: 0.4;
fill: none;
pointer-events: none;
}
.node:hover,
.node--source,
.node--target {
font-weight: 700;
font-weight: 700;
}
.node--source {
fill: #2ca02c;
fill: #2ca02c;
}
.node--target {
fill: #d62728;
fill: #d62728;
}
.link--source,
.link--target {
stroke-opacity: 1;
stroke-width: 2px;
stroke-opacity: 1;
stroke-width: 2px;
}
.link--source {
stroke: #d62728;
stroke: #d62728;
}
.link--target {
stroke: #2ca02c;
stroke: #2ca02c;
}
</style>
......@@ -59,9 +59,9 @@ var diameter = @WIDTH@,
innerRadius = radius - @PADDING@;
var root = packageHierarchy(JSON.parse('@JSON_STRING@'))
.sum(function(d) { return d.size; });
.sum(function(d) { return d.size; });
var cluster = d3.cluster()
.size([360, innerRadius]);
.size([360, innerRadius]);
cluster(root);
var imports = packageImports(root.leaves());
......@@ -71,182 +71,182 @@ var imports = packageImports(root.leaves());
///////////////////////////// Create canvas
var link, node; // Required by callbacks.
var svg = d3.select("body").append("svg")
.call(d3.zoom().scaleExtent([0.1, 10]).on("zoom", function () {
svg.attr("transform",
"translate(" +
(d3.event.transform.applyX(radius)) + "," +
(d3.event.transform.applyY(radius)) + ") " +
"scale(" + d3.event.transform.k + ")")
}))
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
.call(d3.zoom().scaleExtent([0.1, 10]).on("zoom", function () {
svg.attr("transform",
"translate(" +
(d3.event.transform.applyX(radius)) + "," +
(d3.event.transform.applyY(radius)) + ") " +
"scale(" + d3.event.transform.k + ")")
}))
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
update();
///////////////////////////// Add user interaction
document.onkeydown = function(e) {
switch (e.keyCode) {
case 48: // 0
tension = 0.0
update();
break;
case 49: // 1
tension = 0.1
update();
break;
case 50: // 2
tension = 0.2
update();
break;
case 51: // 3
tension = 0.3
update();
break;
case 52: // 4
tension = 0.4
update();
break;
case 53: // 5
tension = 0.5
update();
break;
case 54: // 6
tension = 0.6
update();
break;
case 55: // 7
tension = 0.7
update();
break;
case 56: // 8
tension = 0.8
update();
break;
case 57: // 9
tension = 0.9
update();
break;
case 173: // -
tension = 1.0
update();
break;
case 87: // w
tension = Math.min(tension + 0.01, 1);
update();
break;
case 83: // s
tension = Math.max(tension - 0.01, 0);
update();
break;
}
switch (e.keyCode) {
case 48: // 0
tension = 0.0
update();
break;
case 49: // 1
tension = 0.1
update();
break;
case 50: // 2
tension = 0.2
update();
break;
case 51: // 3
tension = 0.3
update();
break;
case 52: // 4
tension = 0.4
update();
break;
case 53: // 5
tension = 0.5
update();
break;
case 54: // 6
tension = 0.6
update();
break;
case 55: // 7
tension = 0.7
update();
break;
case 56: // 8
tension = 0.8
update();
break;
case 57: // 9
tension = 0.9
update();
break;
case 173: // -
tension = 1.0
update();
break;
case 87: // w
tension = Math.min(tension + 0.01, 1);
update();
break;
case 83: // s
tension = Math.max(tension - 0.01, 0);
update();
break;
}
};
///////////////////////////// Callbacks
function update() {
d3.selectAll("g > *").remove();
svg.append("g")
var line = d3.radialLine()
.curve(d3.curveBundle.beta(tension))
.radius(function(d) { return d.y; })
.angle(function(d) { return d.x / 180 * Math.PI; });
link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node");
link = link
.data(imports)
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line);
node = node
.data(root.leaves())
.enter().append("text")
.attr("class", "node")
.attr("dy", "0.31em")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.data.key; })
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);
d3.selectAll("g > *").remove();
svg.append("g")
var line = d3.radialLine()
.curve(d3.curveBundle.beta(tension))
.radius(function(d) { return d.y; })
.angle(function(d) { return d.x / 180 * Math.PI; });
link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node");
link = link
.data(imports)
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line);
node = node
.data(root.leaves())
.enter().append("text")
.attr("class", "node")
.attr("dy", "0.31em")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.data.key; })
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);
}
function mouseovered(d) {
node
.each(function(n) { n.target = n.source = false; });
link
.classed("link--target", function(l) { if (l.target === d) return l.source.source = true; })
.classed("link--source", function(l) { if (l.source === d) return l.target.target = true; })
.filter(function(l) { return l.target === d || l.source === d; })
.raise();
node
.classed("node--target", function(n) { return n.target; })
.classed("node--source", function(n) { return n.source; });
node
.each(function(n) { n.target = n.source = false; });
link
.classed("link--target", function(l) { if (l.target === d) return l.source.source = true; })
.classed("link--source", function(l) { if (l.source === d) return l.target.target = true; })
.filter(function(l) { return l.target === d || l.source === d; })
.raise();
node
.classed("node--target", function(n) { return n.target; })
.classed("node--source", function(n) { return n.source; });
}
function mouseouted(d) {
link
.classed("link--target", false)
.classed("link--source", false);
link
.classed("link--target", false)
.classed("link--source", false);
node
.classed("node--target", false)
.classed("node--source", false);
node
.classed("node--target", false)
.classed("node--source", false);
}
// Lazily construct the package hierarchy from class names.
function packageHierarchy(classes) {
var map = {};
function find(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
return d3.hierarchy(map[""]);
var map = {};
function find(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
return d3.hierarchy(map[""]);
}
// Return a list of imports for the given array of nodes.
function packageImports(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.data.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.data.imports) d.data.imports.forEach(function(i) {
if (map[i]) {
imports.push(map[d.data.name].path(map[i]));
}
});
});
return imports;
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.data.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.data.imports) d.data.imports.forEach(function(i) {
if (map[i]) {
imports.push(map[d.data.name].path(map[i]));
}
});
});
return imports;
}
</script>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment