nitc :: MPackageTree
A simple specialisation of OrderedTree to display packages, groups and modulesSerializable::inspect
to show more useful information
more_collections :: more_collections
Highly specific, but useful, collections-related classes.serialization :: serialization_core
Abstract services to serialize Nit objects to different formatscore :: union_find
union–find algorithm using an efficient disjoint-set data structurenitc :: api_metrics
nitc :: nitmetrics
A program that collects various metrics on nit programs and libraries
# Visualisation of Nit models
module model_viz
import model
import ordered_tree
# A simple specialisation of OrderedTree to display packages, groups and modules
class MPackageTree
super OrderedTree[MConcern]
# The model where to look for information
var model: Model
redef fun display(a) do
if a isa MGroup then
if a.parent == null then return "{a.mpackage.name} ({a.filepath or else "?"})"
return a.name + " (group)"
else if a isa MModule then
return a.name
else
abort
end
end
private var linex_comparator: nullable LinexComparator = null
# Sort modules and groups with their names
fun sort_with_alpha
do
sort_with(alpha_comparator)
end
# Sort modules and groups with a loosely adaptation of the linearization of modules
fun sort_with_linex
do
var c = linex_comparator
if c == null then
c = new LinexComparator(self)
linex_comparator = c
end
sort_with(c)
end
end
# Compare modules and groups using the
private class LinexComparator
super Comparator
redef type COMPARED: MConcern
var mins = new HashMap [MGroup, nullable MModule]
var maxs = new HashMap [MGroup, nullable MModule]
fun mini(o: Object): nullable MModule do
if o isa MModule then return o
assert o isa MGroup
if not mins.has_key(o) then computeminmax(o)
return mins[o]
end
fun maxi(o: Object): nullable MModule do
if o isa MModule then return o
assert o isa MGroup
if not maxs.has_key(o) then computeminmax(o)
return maxs[o]
end
fun computeminmax(o: MGroup) do
if not tree.sub.has_key(o) then
mins[o] = null
maxs[o] = null
return
end
var subs = tree.sub[o]
var minres = mini(subs.first)
var maxres = maxi(subs.first)
var order = o.model.mmodule_importation_hierarchy
for o2 in subs do
var c = mini(o2)
if c == null then continue
if minres == null or order.compare(minres, c) > 0 then minres = c
c = maxi(o2)
if c == null then continue
if maxres == null or order.compare(maxres, c) < 0 then maxres = c
end
mins[o] = minres
maxs[o] = maxres
#if minres != maxres then print "* {o} {minres}..{maxres}"
end
redef fun compare(a,b) do
var ma = mini(a)
var mb = mini(b)
if ma == null then
if mb == null then return 0 else return -1
else if mb == null then
return 1
end
var order = ma.model.mmodule_importation_hierarchy
return order.compare(ma, mb)
end
var tree: OrderedTree[MConcern]
end
redef class Model
# Generate a MPackageTree based on the packages, groups and modules known in the model
fun to_mpackage_tree: MPackageTree
do
var res = new MPackageTree(self)
for p in mpackages do
for g in p.mgroups do
res.add(g.parent, g)
for m in g.mmodules do
res.add(g, m)
end
end
end
return res
end
end
# Generate graphiz files based on packages, groups and modules
#
# Interesting elements must be selected. See `mmodules`, ``
# Display configuration can be set. See `cluster_group`, `package_group`
class MPackageDot
super Writable
# The model where to look for information
var model: Model
# Set of packages to expand fully (ie all groups and modules are displayed)
# Initially empty, packages can be added
var mpackages = new HashSet[MPackage]
# Set of modules to display
# Initially empty, modules can be added
var mmodules = new HashSet[MModule]
private fun node_for(mmodule: MModule): String
do
return "m_{mmodule.object_id}"
end
# Should groups be shown as clusters?
var cluster_group = true is writable
# Should packages be shown as clusters?
var package_group = true is writable
# Recursively generate node and clusters for a mgroup
private fun dot_cluster(o: Writer, mgroup: MGroup)
do
# Open the cluster, if required
if mgroup.parent == null then
# is is a root group, so display the package
if package_group then
o.write("subgraph cluster_{mgroup.object_id} \{\nlabel=\"{mgroup.mpackage.name}\\n({mgroup.filepath or else "?"})\"\ncolor=black\nstyle=dotted\n")
end
else
if cluster_group then
o.write("subgraph cluster_{mgroup.object_id} \{\nlabel=\"{mgroup.name}\"\ncolor=blue\nstyle=dotted\n")
end
end
# outputs the mmodules to show
for mmodule in mgroup.mmodules do
if not mmodules.has(mmodule) then continue
o.write("\t{node_for(mmodule)} [label=\"{mmodule.name}\",color=green]\n")
end
# recursively progress on sub-clusters
for d in mgroup.in_nesting.direct_smallers do
dot_cluster(o, d)
end
# close the cluster if required
if mgroup.parent == null then
if package_group then o.write("\}\n")
else
if cluster_group then o.write("\}\n")
end
end
# Extends the set of `mmodules` by recursively adding the most specific imported modules of foreign packages
fun collect_important_importation
do
var todo = new List[MModule]
todo.add_all(mmodules)
while not todo.is_empty do
var m = todo.pop
for psm in m.in_importation.greaters do
if m.mgroup.mpackage != psm.mgroup.mpackage then continue
for ssm in psm.in_importation.direct_greaters do
if psm.mgroup.mpackage == ssm.mgroup.mpackage then continue
mmodules.add(ssm)
todo.add(ssm)
end
end
end
end
# Generate the dot content with the current configuration
redef fun write_to(stream)
do
# Collect interesting nodes
for m in model.mmodules do
# filter out modules outside wanted packages
if m.mgroup == null then continue
if not mpackages.has(m.mgroup.mpackage) then continue
mmodules.add(m)
end
collect_important_importation
# Collect interesting edges
var sub_hierarchy = new POSet[MModule]
for m in mmodules do
sub_hierarchy.add_node(m)
for sm in m.in_importation.greaters do
if sm == m then continue
if not mmodules.has(sm) then continue
sub_hierarchy.add_edge(m, sm)
end
end
stream.write("digraph g \{\n")
stream.write("rankdir=BT;node[shape=box];\n")
# Generate the nodes
for p in model.mpackages do
dot_cluster(stream, p.root.as(not null))
end
# Generate the edges
for m in mmodules do
for sm in sub_hierarchy[m].direct_greaters do
var nm = node_for(m)
var nsm = node_for(sm)
if m.in_importation.direct_greaters.has(sm) then
stream.write("\t{nm} -> {nsm}[style=bold]\n")
else
stream.write("\t{nm} -> {nsm}[style=solid]\n")
end
end
end
stream.write("\}\n")
end
end
src/model/model_viz.nit:15,1--259,3