Property definitions

nitc $ MPackageDot :: defaultinit
# 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:132,1--259,3