@uri "http://www.ifi.uzh.ch/rerg/dot"
package dot;

require "kermeta"
using kermeta::standard
using kermeta::utils

abstract class GraphElement
{
	attribute id : String

	attribute label : Label

	attribute style : String

	attribute color : String

	reference graph : Graph#elements
	
	operation toDOT(): String is abstract

}
class Graph inherits GraphElement
{
	attribute rankDir : RankDir

	attribute elements : GraphElement[0..*]#graph
	
	method toDOT(): String is do
		var sb: StringBuffer init StringBuffer.new
		sb.append("digraph ")
		sb.append(id)
		sb.append(" {\n")
		if not rankDir.isVoid
		then
			sb.append("\trankdir=")
			sb.append(rankDir.name)
			sb.append(";\n")
		end
		elements.each{e | sb.append("\t") sb.append(e.toDOT()) sb.append(";\n")}
		sb.append("}\n")
		result := sb.toString
	end
}

abstract class Label
{
	attribute content : String
	
	operation toDOT(): String is abstract
}

class SimpleLabel inherits Label
{
	method toDOT(): String is do
		result := "\"" + content + "\""
	end
}

class HTMLLabel inherits Label
{
	method toDOT(): String is do
		result := "<" + content + ">"
	end
}

enumeration RankDir
{
	TB;
	LR;
	BT;
	RL;
	
}
class SubGraph inherits Graph
{
	attribute cluster : Boolean[1..1]
	
	method toDOT(): String is do
		var sb: StringBuffer init StringBuffer.new
		sb.append("subgraph ")
		if cluster
		then
			sb.append("cluster_")
		end
		sb.append(id)
		sb.append(" {\n")
		if not label.isVoid
		then
			sb.append("\tlabel=")
			sb.append(label.toDOT())
			sb.append(";\n")
		end
		elements.each{e | sb.append("\t") sb.append(e.toDOT()) sb.append(";\n")}
		sb.append("\t}")
		result := sb.toString
	end

}
class Node inherits GraphElement
{
	attribute shape : NodeShape[0..1]

	reference outcoming : Arc[0..*]#fromNode

	reference incoming : Arc[0..*]#toNode

	method toDOT(): String is do
		var sb: StringBuffer init StringBuffer.new
		sb.append(id)
		if not label.isVoid
		then
			sb.append("[")
			sb.append("label=")
			sb.append(label.toDOT())
			sb.append("]")
		end
		
		if not shape.isVoid
		then
			sb.append(shape.toDOT)
		end
		result := sb.toString
	end
}

class Arc inherits GraphElement
{
	reference fromNode : Node[1..1]#outcoming

	reference toNode : Node[1..1]#incoming

	attribute arrowHead : Arrow[0..1]

	attribute headLabel : Label

	attribute tailLabel : Label

	attribute arrowTail : Arrow[0..1]
	
	method toDOT(): String is do
		var sb: StringBuffer init StringBuffer.new
		
		sb.append(fromNode.id)
		sb.append(" -> ")
		sb.append(toNode.id)
		
		if not label.isVoid
		then
			sb.append("[label=")
			sb.append(label.toDOT)
			sb.append("]")
		end
		
		if not tailLabel.isVoid
		then
			sb.append("[taillabel=")
			sb.append(tailLabel.toDOT)
			sb.append("]")
		end
		
		if not headLabel.isVoid
		then
			sb.append("[headlabel=")
			sb.append(headLabel.toDOT)
			sb.append("]")
		end
		
		if not arrowHead.isVoid
		then
			sb.append("[")
			sb.append("arrowhead=")
			sb.append(arrowHead.toDOT())
			sb.append("]")
		end
		
		if not arrowTail.isVoid
		then
			sb.append("[")
			sb.append("arrowtail=")
			sb.append(arrowTail.toDOT())
			sb.append("]")
		end
		
		sb.append("[dir=both]")
		
		result := sb.toString
	end

}

abstract class NodeShape
{
	operation toDOT(): String is abstract
}
class BoxShape inherits NodeShape
{
	method toDOT(): String is do
		result := "[shape=box]"
	end
}
class PointShape inherits NodeShape
{
	method toDOT(): String is do
		result := "[shape=point]"
	end
}
class PolygonShape inherits NodeShape
{
	attribute sides : Integer
	
	method toDOT(): String is do
		result := "[shape=polygon, sides=" + sides.toString + "]"
	end
}
class RecordShape inherits NodeShape
{
	method toDOT(): String is do
		result := "[shape=record]"
	end
}

class Arrow
{
	attribute clip : ArrowClip

	attribute plain : Boolean

	attribute shape : ArrowShape
	
	operation toDOT(): String is do
		var sb: StringBuffer init StringBuffer.new
		if not plain
		then
			sb.append("o")
		end
		
		if clip == ArrowClip.left
		then
			sb.append("l")
		end
		
		if clip == ArrowClip.right
		then
			sb.append("r")
		end
		
		sb.append(shape.name)
		
		result := sb.toString
	end
}
enumeration ArrowClip
{
	none;
	left;
	right;
}
enumeration ArrowShape
{
	normal;
	diamond;
	none;
	vee;
	box;
	~inv;
	tee;
	crow;
	dot;
}
class EllipseShape inherits NodeShape
{
	method toDOT(): String is do
		result := "[shape=ellipse]"
	end
}
class CircleShape inherits NodeShape
{
	method toDOT(): String is do
		result := "[shape=circle]"
	end
}
class PlaintextShape inherits NodeShape
{
	method toDOT(): String is do
		result := "[shape=none, margin=0]"
	end
}
