@=~
~p typesetter = html
~p maximum_input_line_length = infinity
~t title titlefont left "Ein universeller Graph-Editor"
In diesem Beispiel wird eine Editor spezifiziert, mit dem
man beliebige Graphen konstruieren kann.
Es kann zwischen gerichteten und ungerichteten Kanten
umgeschaltet werden.
Der Editor unterstuetzt zwei verschiedene Knotentypen,
deren Aussehen jeweils konfigurierbar ist.
Die Spezifikation ist folgendermassen strukturiert:
~t table_of_contents
~A~
~B~
Zunaechst muss die abstrakte Struktur der Sprache spezifizert werden, auf der
alle anderen Spezifikationen basieren. Hierzu wird eine flache Baumstruktur
verwendet mit Querbeziehungen und persistenten Attributen der Baumknoten
verwendet. Das Ganze wird in einer Datei namens "graph.vlag" spezifiziert.
~O~~{
~
~}
Graphen bestehen aus einer Knoten- und einer Kantenmenge.
Ausserdem sind ihnen Parametrisierungsoptionen zugeordnet: Man kann zwischen
gerichteten und ungerichteten Kanten waehlen und das Aussehen der
zwei verschiedenen Knotentypen bestimmten.
~$~+=~{
VLROOT Root(Graph(NodeList(Node*), EdgeList(Edge*)))
{
PERS GraphType: VLInt EDITWITH OPTION ("directed","undirected");
PERS SmoothLines: VLBoolean EDITWITH CHECKBOX;
PERS NodeType1Color: VLString EDITWITH COLOR;
PERS NodeType1Shape: VLInt EDITWITH OPTION ("rectangle","rounded-rectangle","circle");
PERS NodeType2Color: VLString EDITWITH COLOR;
PERS NodeType2Shape: VLInt EDITWITH OPTION ("rectangle","rounded-rectangle","circle");
}
~}
Es gibt zwei verschiedene Knotentypen. Jeder Knoten hat ein persistentes Attribut,
das den Namen des Knotens speichert. Zusaetzlich gibt es den Definitionstabellenzeiger
~{Endpoint~}. Dieser gibt an, welche Linien an einem Knoten enden.
~$~+=~{
ABSTRACT Node {}
VLRULE NodeType1() ACTS AS Node
{
PERS Name: VLString EDITWITH ENTRY;
PERS Endpoint: DEFTABLEKEY;
}
VLRULE NodeType2() ACTS AS Node
{
PERS Name: VLString EDITWITH ENTRY;
PERS Endpoint: DEFTABLEKEY;
}
~}
Jetzt fehlen noch die Kanten: Jeder Kante hat eine Beschriftung
und ein Start- und Zielknoten zugeordnet. Das untergeordnete
Baumsymbol ~{EdgeLabel~} dient zum Layout der Beschriftung.
~$~+=~{
VLRULE Edge(EdgeLabel())
{
PERS Name: VLString EDITWITH ENTRY;
PERS From: DEFTABLEKEY;
PERS To: DEFTABLEKEY;
}
~}
~B~
Nachfolgend werden die initialen Werte der persistente Attribute definiert.
Dazu wird ein allgemeineres Konzept benutzt: Es gibt einen speziellen
Attributauswerter, der beliebige Aenderungen an der abstrakten Struktur
durchfuehren kann. Waehrend des Attributauswerterdruchlaufs wird auch
das Setzen von Defaultwerten durchgefuehrt.
~O~~{
#ifndef BASIC_CHECK_LIDO_MACROS_H
#define BASIC_CHECK_LIDO_MACROS_H
#ifdef __STDC__
#define STR(attr) #attr
#else
#define STR(attr) "attr"
#endif
#define VL_SET_DEFAULT_VALUE(ATTRNAME,VALUE) \
IF(SELECT(THIS.ATTRNAME,isNull()), \
PTGBasicCheckExecute(2,PTGSetAttr(_currn,STR(ATTRNAME),VALUE)))
#endif
SYMBOL Root
COMPUTE
VL_SET_DEFAULT_VALUE(persNodeType1Shape,"0");
VL_SET_DEFAULT_VALUE(persNodeType2Shape,"2");
VL_SET_DEFAULT_VALUE(persNodeType1Color,"black");
VL_SET_DEFAULT_VALUE(persNodeType2Color,"black");
VL_SET_DEFAULT_VALUE(persGraphType,"0");
VL_SET_DEFAULT_VALUE(persSmoothLines,"0");
END;
~}
~A~
Zur Berechnung der visuellen Darstellung benutzen wir einen
eigenen Attributauswerter, der hier ~{view0~} genannt ist.
Dies soll darauf hindeuten, dass es i.a. noch andere Sichten
auf die abstrakte Struktur geben koennte.
~O~~{
~
~}
Zur Benutzung der Spezifikationsmodule fuer die visuellen Muster muessten
diese instanziiert und dem Attributauswerter ~{view0~} zugeordnet
werden.
~O~~{
$/Visual/VisPattern2.gnrc+instance=view0:inst
$/Visual/StdDrawings.fw
$/Visual/VisConnectionPattern2Level.gnrc+instance=1+referto=view0:inst
~}
~B~
Knoten vom Typ 1 und Knoten vom Typ 2 koennen ueber
weite Strecken gleich behandelt werden. Daher werden
diese durch eine Klassensymbol zusammengefasst
und Unterschiede herausfaktorisiert.
~$~+=~{
CLASS SYMBOL NodeTypeClass:
NodeTypeShape: int,
NodeTypeColor, FrameDrawingAttr: VLCharPtr;
SYMBOL NodeType1 INHERITS NodeTypeClass
COMPUTE
INH.NodeTypeShape = INCLUDING Root.persNodeType1Shape;
INH.NodeTypeColor = INCLUDING Root.persNodeType1Color;
END;
SYMBOL NodeType2 INHERITS NodeTypeClass
COMPUTE
INH.NodeTypeShape = INCLUDING Root.persNodeType2Shape;
INH.NodeTypeColor = INCLUDING Root.persNodeType2Color;
END;
~}
~B~
Zur Visualisierung der Baumstruktur wenden wir das
Mengenmuster an: Der Knoten ~{Graph~} spielt die
Rolle einer (visuellen) Menge, Knoten der Klasse
~{NodeTypeClass~} (also ~{NodeType1~} und
~{NodeType2~}) repraesentieren konkrete Mengenelemente.
~$~+=~{
SYMBOL Graph INHERITS VP_Set
COMPUTE
THIS.VP_OptStartSize=VisIntVector(600,500);
THIS.VP_OptFrameStyle = "-fill {} -outline {}";
THIS.VP_OptContainerKind = 1;
END;
SYMBOL _List_Node INHERITS VP_ListContext END;
SYMBOL Node INHERITS VP_MovableContext END;
SYMBOL NodeTypeClass INHERITS VP_Movable END;
~}
~B~
Zur Beschreibung der Darstellung von Knoten wird
die Symbolrolle ~{TextTerminal~} verwendet.
Kern der Darstellung ist eine beliebige Zeichenkette.
Durch das Attribut ~{OptFrameDrawing~} kann
ein Rahmen um den Text spezifiziert werden.
In diesem Fall haengt das Aussehen des Rahmens
von den Konfiguartionseinstellungen im Graph-Kontext
ab.
~$~+=~{
SYMBOL NodeTypeClass INHERITS VP_TextTerminal
COMPUTE
SYNT.VP_OptText = THIS.persName;
SYNT.VP_OptFont = "default";
SYNT.VP_OptTextAttr = "persName";
SYNT.FrameDrawingAttr = VLString(VLList("-outline",THIS.NodeTypeColor,
VLList("-fill {} -width 2")));
SYNT.VP_OptDefault = "";
SYNT.VP_OptFrameDrawing =
IF( EQ(THIS.NodeTypeShape,0),
NEW(FrameDrawing(THIS.FrameDrawingAttr,5,VisIntVector(20,20))),
IF( EQ(THIS.NodeTypeShape,1),
NEW(RoundedFrameDrawing(THIS.FrameDrawingAttr,5, 20)),
NEW(CircleFrameDrawing(THIS.FrameDrawingAttr,5,VisIntVector(20,20)))
)
);
END;
~}
~B~
Um Baumsymbole des Typs ~{Edge~} als Linien darzustellen, wird
das Linienmuster verwendet: Baumknoten vom Typ ~{Edge~}
spielen die Rolle der Linienverbindung (~{RelationTypeDirect~}),
Baumsymbole der Klasse ~{NodeTypeClass~} spielen die
Rolle von (potentiellen) Linienendpunkten. Der Knoten ~{Graph~}
repraesentiert den Gueligkeitsbereich der Linienmenge.
~$~+=~{
SYMBOL Graph INHERITS VP_RelationArea END;
SYMBOL _List_Edge INHERITS VP_ListSimpleRelations END;
SYMBOL NodeTypeClass INHERITS VP_EndPoint, VP_EndPointLevel1
COMPUTE
SYNT.VP_OptToTheEnd = 0;/* Linien enden am Rand */
SYNT.VP_OptShape = IF(EQ(THIS.NodeTypeShape,2),1,0);
END;
SYMBOL Edge INHERITS VP_RelationTypeDirect, VP_RelationLevel1
COMPUTE
SYNT.VP_OptLineType = VLString(VLList("-smooth",
INCLUDING Root.persSmoothLines,VLList("-width 2")));
SYNT.VP_OptArrowType =
IF(EQ(INCLUDING Root.persGraphType,0),VLCharPtr("-arrow last"),VLCharPtr("-arrow none"));
/* without cast, there are some problems with "const char *" */
END;
~}
~B~
Das Symbol ~{EdgeLabel~} repraesentiert die Beschriftung
von Linien. Dazu erbt es von der Klasse ~{RelationLabel2~}.
Zur Definition des Aussehens von Beschriftungen wird die
Symbolrolle ~{TextTerminal2~} verwendet.
~$~+=~{
SYMBOL EdgeLabel INHERITS VP_RelationLabel2 END;
SYMBOL EdgeLabel INHERITS VP_TextTerminal2
COMPUTE
SYNT.VP_OptDefault = "";
SYNT.VP_OptText = INCLUDING Edge.persName;
END;
~}
~B~
Aus technischen Gruenden ist es notwendig, die in Lido spzifizierten
Vererbungen zusaetzlich in Vtree anzugeben. Die nachfolgenden
Zeilen koennten automatisch aus der Lido-Spezifikation
hergeleitet werden.
~O~~{
SYMBOL Graph INHERITS VP_view0_Set END;
SYMBOL Node INHERITS VP_view0_MovableContext END;
SYMBOL Edge INHERITS VP_view0_RelationType END;
SYMBOL EdgeLabel INHERITS VP_view0_RelationLabel2 END;
SYMBOL NodeType1 INHERITS VP_view0_EndPoint END;
SYMBOL NodeType2 INHERITS VP_view0_EndPoint END;
~}