Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
D
d3vis
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Marcel Steinbeck
d3vis
Commits
ea93878c
Commit
ea93878c
authored
6 years ago
by
Marcel Steinbeck
Browse files
Options
Downloads
Patches
Plain Diff
Spaces -> tabs.
parent
6e95f8e8
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
dependencies/dependencies.groovy
+50
-50
50 additions, 50 deletions
dependencies/dependencies.groovy
dependencies/hierarchical_edge_bundling.html.in
+165
-165
165 additions, 165 deletions
dependencies/hierarchical_edge_bundling.html.in
with
215 additions
and
215 deletions
dependencies/dependencies.groovy
+
50
−
50
View file @
ea93878c
///////////////////////////// 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
This diff is collapsed.
Click to expand it.
dependencies/hierarchical_edge_bundling.html.in
+
165
−
165
View file @
ea93878c
...
...
@@ -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>
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment