Spezifikationsmodule - Software and Systems Engineering - TUM
Spezifikationsmodule - Software and Systems Engineering - TUM
Spezifikationsmodule - Software and Systems Engineering - TUM
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Modellbasierte<br />
Entwicklung:<br />
<strong>Spezifikationsmodule</strong><br />
Diplomarbeit<br />
Florian Hölzl
Technische Universität München<br />
Fakultät für Informatik<br />
Modellbasierte<br />
Entwicklung:<br />
<strong>Spezifikationsmodule</strong><br />
Diplomarbeit<br />
Aufgabensteller:<br />
Betreuer:<br />
Bearbeiter:<br />
Prof. Dr. Dr. h.c. Manfred Broy<br />
Dr. Bernhard Schätz<br />
Florian Hölzl<br />
Abgabedatum: 13. Mai 2005
Ich versichere, dass ich diese Diplomarbeit selbständig verfasst und nur die angegebenen<br />
Quellen und Hilfsmittel verwendet habe.<br />
Unterschleissheim, den 12. Mai 2005<br />
(Florian Hölzl)
”Nimm an hier ist eine Aussage. Wir wissen nicht, ob sie zu der einen Kategorie<br />
oder der <strong>and</strong>eren gehört. Aber wenn wir die verschiedenen Kategorien in<br />
eine zusammenwerfen, hören die Unterschiede der Kategorien auf zu existieren.<br />
Das muss ich erklären. Wenn es einen Anfang gibt, dann gibt es eine Zeit vor<br />
diesem Anfang und eine Zeit vor der Zeit, welche vor der Zeit dieses Anfangs<br />
war. Wenn es das Vorh<strong>and</strong>ensein gibt, musste es auch das Nicht-Vorh<strong>and</strong>ensein<br />
gegeben haben. Und wenn es eine Zeit gab zu der nichts existierte, dann muss<br />
es auch eine Zeit gegeben haben, zu der nicht mal das Nichts existierte. Ganz<br />
plötzlich, gab es dann das Nichts. Kann dann wirklich jem<strong>and</strong> sagen, ob es zur<br />
Kategorie des Vorh<strong>and</strong>enseins oder des Nicht-Vorh<strong>and</strong>enseins gehört? Sogar diese<br />
Worte die ich gerade sagte - ich kann nicht sagen, ob sie etwas bedeuten oder<br />
nicht.”<br />
(Chuang Tzu, 2. Kapitel: Über das Ebnen aller Dinge) 1<br />
1<br />
http://www.panlatrevo.com/texts/chuangtzu/<br />
ins Englische: Yutang Lin<br />
ins Deutsche: Florian Hölzl
Zusammenfassung<br />
Im Bereich eingebetteter Systeme findet sich vor allem im Automobilbau eine<br />
zunehmende Anzahl variantenreicher Einzelkomponenten. Neben anwendungsspezifischen<br />
Teilfunktionen beinhalten sie auch allgemeine bzw. systemweit einheitliche<br />
Funktionen, beispielsweise einen Modus zum Auslesen von Fehlerspeichern<br />
oder systemweit einheitliche Nachrichten des Powermanagements.<br />
Die Vereinigung von allgemeinem und speziellem Verhalten innerhalb einer<br />
Funktionskomponente ist bisher noch kaum durch Werkzeuge unterstützt,<br />
sondern geschieht häufig auf Ebene des Anwendungscodes. Modellbasierte Entwicklung<br />
in Verbindung mit Modelltransformationstechniken scheinen hier ein<br />
möglicher Weg zur Verbesserung der Situation zu sein.<br />
In dieser Diplomarbeit wird gezeigt, wie, mit Hilfe der Transformationssprache<br />
ODL und Spezifikationsmodellen in AutoFocus2, eine Spezifikationsvereinigung<br />
interaktiv durch den Entwickler, durchgeführt werden kann. Die Nutzerinteraktion<br />
ist notwendig, da zwar allgemeine Lösungen des Vereinigungsproblems<br />
existieren, aber nicht effizient berechnet werden können. Durch die Automatisierung<br />
und Formalisierung einzelner Teilschritte auf Spezifikationsebene kann<br />
gegenüber der rein manuellen Durchführung auf Codeebene eine Verbesserung<br />
der Qualität erzielt werden, indem die Fehleranfälligkeit reduziert wird.<br />
Neben der konkreten Umsetzung anh<strong>and</strong> einer Beispielkomponente wird<br />
auch der theoretische Hintergrund, eine allgemeinere Form des aus der Prädikatenlogik<br />
bekannten Unifikationsverfahrens betrachtet. Die Unifikation stellt den<br />
roten Faden dieser Arbeit dar.<br />
In dieser Arbeit wird rein die technische Umsetzung samt der theoretischen<br />
Grundlagen aus Sicht der Informatik betrachtet. Die Nutzung und Einbindung<br />
solch komplexer Modelltransformationen in den Entwicklungsprozess und die<br />
sich daraus möglicherweise ergebenden betriebswirtschaftlichen Vorteile sind<br />
nicht mehr Teil dieser Arbeit.
Inhaltsverzeichnis<br />
1 Einführung 3<br />
1.1 Modellbasierte, bausteinorientierte <strong>Software</strong>entwicklungsprozesse 3<br />
1.2 Konkrete Aufgabenstellung . . . . . . . . . . . . . . . . . . . . . 4<br />
1.3 Struktur des vorliegenden Dokuments . . . . . . . . . . . . . . . 5<br />
2 Modelltransformationen, Werkzeuge und Beispielproblem 6<br />
2.1 Begriffsbestimmung: Modelltransformation . . . . . . . . . . . . . 6<br />
2.2 Das Modellierungswerkzeug AutoFocus 2 . . . . . . . . . . . . . 7<br />
2.3 Die Transformationssprache Operation Definition Language . . . 12<br />
2.4 Modelltransformation: AutoFocus 2 Spezifikationsvereinigung . . 14<br />
2.5 Einschränkungen für die Modellierungselemente . . . . . . . . . . 18<br />
2.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />
3 Einführungsbeispiel: Typunifikation 20<br />
3.1 AutoFocus 2 Datentypen und Metamodell . . . . . . . . . . . . . 20<br />
3.2 Unifikation und Typvereinbarkeit . . . . . . . . . . . . . . . . . . 23<br />
3.3 Typvereinbarkeitsprädikat in ODL . . . . . . . . . . . . . . . . . 24<br />
3.4 Interaktive Typvereinigung . . . . . . . . . . . . . . . . . . . . . 26<br />
3.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />
4 Spezifikationsvereinigung aus theoretischer Sicht 32<br />
4.1 Definition des Lösungsraumes . . . . . . . . . . . . . . . . . . . . 32<br />
4.2 Unifikationsrelationen . . . . . . . . . . . . . . . . . . . . . . . . 33<br />
4.3 Abhängigkeiten der Modellelemente . . . . . . . . . . . . . . . . 35<br />
4.4 Lösungsverfahren . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />
4.5 Komplexitätsbetrachtung . . . . . . . . . . . . . . . . . . . . . . 38<br />
4.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />
5 Interaktive Spezifikationsvereinigung 40<br />
5.1 Übergang zum interaktiven Ablauf . . . . . . . . . . . . . . . . . 40<br />
5.2 Festlegung der Dialogführung . . . . . . . . . . . . . . . . . . . . 41<br />
5.3 Alternative Interaktionsabläufe . . . . . . . . . . . . . . . . . . . 47<br />
5.4 Umsetzung der Dialogführung in ODL . . . . . . . . . . . . . . . 48<br />
5.5 Konstruktion der Vereinigungskomponente . . . . . . . . . . . . . 53<br />
5.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />
1
6 Fazit und Ausblick 58<br />
6.1 Einordnung dieser Arbeit . . . . . . . . . . . . . . . . . . . . . . 58<br />
6.2 Ansätze zur Weiterentwicklung der ODL . . . . . . . . . . . . . . 59<br />
6.3 ODL für <strong>and</strong>ere Modellierungssprachen und -werkzeuge . . . . . 59<br />
6.4 ODL-unterstützter Entwicklungsprozess . . . . . . . . . . . . . . 61<br />
A ODL Programm der Spezifikationsvereinigung 62<br />
A.1 Anmerkungen zum Programm . . . . . . . . . . . . . . . . . . . . 62<br />
A.2 ODL Programm . . . . . . . . . . . . . . . . . . . . . . . . . . . 64<br />
2
Kapitel 1<br />
Einführung<br />
1.1 Modellbasierte, bausteinorientierte <strong>Software</strong>entwicklungsprozesse<br />
Modelle spielen in vielen Bereichen der Wissenschaft und Wirtschaft eine wichtige<br />
Rolle. Angefangen bei den Wirtschaftswissenschaften, über die Wettervorhersage<br />
bis hin zu den Ingenieursdisziplinen, dienen Modelle zum vertieften<br />
Verständnis der Abläufe von sozialen, natürlichen oder technischen Systemen.<br />
Der Einsatz von Modellen hat darüber hinaus den Vorteil, dass die Eigenschaften<br />
oder das Verhalten des betrachteten <strong>Systems</strong> bzw. Endprodukts durch Simulation<br />
getestet oder vorhergesagt werden können.<br />
Modellen und Modellierungstechniken kommt auch bei der <strong>Software</strong>entwicklung<br />
eine tragende Rolle zu. Insbesondere sind sie hierbei nicht nur dazu geeignet,<br />
das Problem zu veranschaulichen oder Teileigenschaften zu simulieren<br />
oder bei formalen Modellen nachzuweisen. Vielmehr sollen diese Beschreibungen<br />
von <strong>Software</strong>systemen auch dazu dienen, einzelne Systemteile automatisch<br />
zu erzeugen.<br />
Aus betriebswirtschaftlicher Sicht hat modellbasiertes Vorgehen Auswirkungen<br />
auf Kosten, Termin und Qualität. Kostenersparnisse können sich direkt<br />
aus dem Zeitgewinn ergeben, den <strong>Software</strong>entwickler durch automatisierte Entwicklungsschritte<br />
erzielen. Eine Qualitätssteigerung kann dadurch erzielt werden,<br />
dass automatische Transformationsschritte viele Fehlerquellen manuell ausgeführter<br />
Entwicklungsschritte schließen. Dazu trägt insbesondere der Einsatz<br />
von formalen Methoden bei, wodurch die Korrektheit der Automatisierungen<br />
und Transformationen mathematisch nachgewiesen werden kann.<br />
Neben diesem innerhalb eines <strong>Software</strong>projektes vorh<strong>and</strong>enen Gewinnpotential<br />
ist für hochspezialisierte und auf einen kleinen Problembereich fokussierte<br />
Unternehmen ein weiterer Aspekt der <strong>Software</strong>entwicklung interessant,<br />
nämlich die Wiederverwendung von Einzelkomponenten und Funktionsbausteinen<br />
in mehreren, ähnlich gelagerten Projekten oder <strong>Software</strong>produkten. Diese<br />
Bausteinorientierung hat im optimalen Fall zur Folge, dass neue <strong>Software</strong>lösungen<br />
aus einem vorh<strong>and</strong>enen Satz von Einzel- oder Teillösungen schnell und kosteneffizient<br />
zusammengesetzt werden können.<br />
Diese Idealvorstellung ist in dieser Pauschalität für die <strong>Software</strong>entwicklungsbranche<br />
kaum umsetzbar. Nichts desto trotz kann eine Überprüfung des<br />
3
Entwicklungsprozesses und der Einzeltätigkeiten, im Hinblick auf die Wiederverwendung<br />
von Einzelbausteinen und den Einsatz von Modelltransformationen,<br />
Kostenersparpotentiale zu Tage fördern.<br />
In der vorliegenden Arbeit werden wir zeigen, wie Modelltransformationen<br />
als effiziente Entwicklungsschritte eingesetzt werden können um die Zusammensetzung<br />
einer Gesamtlösung aus Einzelbausteinen bereits auf der Ebene der<br />
Spezifikation durchzuführen. Wir interessieren uns hierbei weniger für das anein<strong>and</strong>er<br />
Koppeln fertiger Komponenten, soondern wollen vielmehr die Spezifikation<br />
von zwei <strong>Software</strong>komponenten konsistent zusammenführen. Wir illustrieren<br />
dieses Prinzip der Spezifikationsvereinigung anh<strong>and</strong> eines kleinen, aber<br />
durchaus praxisrelevanten Beispiels. Die hier vorgestellte Transformation zeigt<br />
die Verschmelzung der Spezifikation eines anwendungsspezifischem Moduls, der<br />
Steuereinheit einer Presse, mit der Spezifikation eines allgemeinen Moduls für<br />
Fehlerbeh<strong>and</strong>lung.<br />
Die vorliegende Arbeit konzentriert sich auf die theoretische Fundierung und<br />
die technische Umsetzung der Spezifikationsvereinigung als ein Beispiel für komplexe<br />
Modelltransformationen. Besonderes Augenmerk liegt hierbei auf der Korrektheit<br />
der Transformation, sowie einer möglichst hohen Arbeitsersparnis des<br />
Entwicklers durch weitestgehende Automatisierung einzelner Teilschritte. Die<br />
Interaktion mit dem Entwickler wird allerdings zentraler Best<strong>and</strong>teil unserer<br />
Betrachtungen sein. Wir haben nicht zum Ziel den Entwickler zu ersetzen, sondern<br />
ihn vielmehr bei zeitintensiven Systementwicklungsschritten optimal zu<br />
unterstützen.<br />
Das weiterführende Ziel des hier technisch untersuchten Vorgehens ist die<br />
Zusammenstellung von Einzeltransformationen zu prozessrelevanten Arbeitsschritten.<br />
Dies ist Inhalt einer weiteren Diplomarbeit ist, die parallel zu der<br />
vorliegenden Arbeit erstellt wurde. Wir geben einen kurzen Ausblick auf deren<br />
Inhalt im letzten Kapitel.<br />
Die Einsetzbarkeit und die betriebswirtschaftlichen Auswirkungen im <strong>Software</strong>entwicklungsprozess<br />
sind hier nicht Teil unserer Betrachtungen. Dazu sind<br />
zunächst entsprechende Fallstudien notwendig, wofür wir mit dieser Arbeit die<br />
technische Basis schaffen wollen.<br />
1.2 Konkrete Aufgabenstellung<br />
Im Rahmen der Diplomarbeit soll das bestehende AQuA-System (AutoFocus/Quest<br />
Framework) 1 eingesetzt werden, um praxisrelevante Analyse- und<br />
Transformationsschritte in einem modellbasierten Entwicklungsprozess zu unterstützen.<br />
Solche Schritte unterstützen den <strong>Software</strong>entwickler, effizient hochwertige<br />
Systeme zu konstruieren. Im Zentrum der DA steht die Unterstützung<br />
von (verhaltens-) bausteinorientierter Entwicklung mittels <strong>Spezifikationsmodule</strong>n.<br />
Hierbei sollen St<strong>and</strong>ardmodule (z.B. Konfiguration, Fehlerbeh<strong>and</strong>lung, Reinitialisierung)<br />
durch Übernahme der Schnittstellen-, Daten-, und Verhaltensbeschreibung<br />
in spezifische Applikationsmodelle eingebunden werden.<br />
Teilaufgaben sind dabei:<br />
• Analyse der Typenverträglichkeit von Datenbeschreibungen (generelle und<br />
1 Statt des AutoFocus/Quest-<strong>Systems</strong> wurde nach Ausschreibung der DA dessen Nachfolger,<br />
das AutoFocus 2 System, als Demonstrationsplattform gewählt<br />
4
interaktive Unifizierbarkeit von Datentypen)<br />
• Konstruktion von kombinierten Typen (Unifikation von Datentypen und<br />
interaktive Verschmelzung)<br />
• Schnittstellenunifikation (Kombination von Schnittstellen abhängig von<br />
der Typenverträglichkeit)<br />
• Verhaltenskombination (Einbindung von Verhaltensbeschreibung und Konstruktion<br />
von zugehörigen Transitionen)<br />
• Bausteinorientierte Entwicklung: Modulisolation (Herauslösen von anwendbaren<br />
Verhaltensbausteinen mittels Vervollständigung von Teilmodulen)<br />
Neben der Realisierung der Funktionalität umfasst die Aufgabenstellung die<br />
Ausarbeitung geeigneter St<strong>and</strong>ardmodule zur Demonstration der Tauglichkeit<br />
des Ansatzes.<br />
1.3 Struktur des vorliegenden Dokuments<br />
In Kapitel 2 werden wir die gestellte Aufgabe der Spezifikationsvereinigung einer<br />
genauen Problemanalyse unterziehen. Hier werden wir, neben der Klärungen<br />
des Begriffs Modelltransformation, das Modellierungswerkzeug AutoFOCUS 2<br />
und die zugehörige Transformationssprache ODL erläutern, sowie das Anwendungsbeispiel<br />
genau darstellen.<br />
Als Grundlage für die weiteren Betrachtungen, werden wir in Kapitel 3 das<br />
Thema Unfikation genau untersuchen. Vom Unifikationsverfahren der Prädikatenlogik<br />
werden wir zur Unifikation von Typdefinitionen übergehen. Für die<br />
weiteren Betrachtungen dieser Diplomarbeit ist das Verständnis der Unifikation<br />
von zentraler Bedeutung, da die einzelnen Verfahren sich auf dieses Prinzip<br />
zurückführen lassen.<br />
In Kapitel 4 untersuchen wir das Transformationsproblem aus theoretischer<br />
Sicht und entwickeln einen allgemeinen, formalen Lösungsansatz. Dieser stützt<br />
sich dabei einerseits auf die Unifikation, <strong>and</strong>ererseits auf ein aussagenlogischen<br />
Belegungsproblem. Abschließend unterziehen wir das Berechnungsverfahren der<br />
Lösungen einer Komplexitätsbetrachtung.<br />
Die Spezifikationsvereinigung werden wir in Kapitel 5 mit der theoretischen<br />
Fundierung in ein praktisch anwendbares Transformationsverfahren übertragen.<br />
Wir befassen uns hier mit den Themen verschiedenartiger Benutzerführungen<br />
und der technischen Umsetzung in ODL. Die Interaktion mit dem Nutzer ist in<br />
diesem ein wichtiger Punkt, da sie die Nutzung von Modelltransformationen im<br />
Modellierungsalltag der <strong>Software</strong>entwickler erlaubt.<br />
Den Abschluß dieser Diplomarbeit bildet das Kapitel 6. Wir werden hier<br />
die Ergebnisse zusammenfassen und in einen Ausblick münden lassen, der sowohl<br />
die technische Basis vertiefen und verbreitern, wie auch die praktische<br />
Weiterführung beleuchten wird.<br />
5
Kapitel 2<br />
Modelltransformationen,<br />
Werkzeuge und<br />
Beispielproblem<br />
Dieses Kapitel beschäftigt sich mit den Grundlagen und Werkzeugen der vorliegenden<br />
Diplomarbeit.<br />
Wir werden als erstes den Modelltransformationsbegriff näher beleuchten<br />
und für uns eine Definition dieses Begriffs treffen.<br />
Als nächstes untersuchen wir ausführlich die zum Einsatz kommenden Werkzeuge,<br />
AutoFocus 2 und die darin implementierte Transformationssprache Transformationssprache<br />
ODL (Operation Definition Language, [Sch01]). Wir zeigen<br />
hier auch schon Beispiele für einfache Modelltransformationen.<br />
Die letzten beide Abschnitte dieses Kapitels führen schließlich die Spezifikationsvereinigung<br />
exemplarisch vor und fassen die Einschränkungen des Vereinigungsproblems<br />
im Hinblick auf die AutoFocus 2-Modellierungsfähigkeiten<br />
zusammen.<br />
2.1 Begriffsbestimmung: Modelltransformation<br />
Der Begriff Modelltransformation erfährt seit einiger Zeit gesteigertes Interesse.<br />
Er ist jedoch nicht eindeutig festgelegt und wird in unterschiedlichen Kontexten<br />
verwendet.<br />
Im Zusammenhang mit der Model-Driven-Architecture (MDA) der Object<br />
Management Group (OMG) finden sich Modelltransformationen als Übergang<br />
von plattform-unabhängigen (platform independet model, PIM) zu plattformabhängigen<br />
(platform dependent model, PDM) Modellen, beispielsweise die<br />
Realisierung einer PIM Komponente für Webapplikationen als Enterprise Java<br />
Bean. Ziel ist hier eine Automatisierung, also bspw. Codegenerierung, und die<br />
Flexibilität aus einem PIM die jeweiligen PDMs zu erhalten. Die Modelltransformationen<br />
sind somit Verfeinerungen, die aus einem abstrakten ein konkretes<br />
Modell, oder Abstraktionen, die aus einem konkreten ein abstrakteres Modell,<br />
erzeugen.<br />
Modelltransformationen können aber auch auf einer einzigen Abstraktions-<br />
6
ebene stattfinden, indem beispielsweise ein Modell in eine <strong>and</strong>ere Modellierungssprache<br />
übertragen wird. Die Übertragung von AutoFocus-Modellen in die Modellierungssprache<br />
Promela, zum Zwecke des Model-Checking mit SPIN (vgl.<br />
[Str05]), ist hierfür ein Beispiel.<br />
Auch die Transformationssprache XSLT ([W3C99]) bringt eine Variante für<br />
Modelltransformationen. Sie definiert eine Sprache zur Umsetzung von XML Dokumenten<br />
in <strong>and</strong>ers strukturierte XML Dokumente. In diesem Zusammenhang<br />
kann man auch von einer Transformation in ein <strong>and</strong>eres Metamodell sprechen.<br />
Metamodelle beschreiben die Struktur von Modellen oder Datenmengen.<br />
Da der Begriff Modelltransformation sehr unterschiedlich interpretiert wird,<br />
wollen wir hier die folgende Festlegung für diese Arbeit treffen. Eine Modelltransformation<br />
ist eine Beschreibung einer modellverändernden Berechnung,<br />
wobei sich diese auf eine Instanz, also ein Modell, für ein festgelegtes Metamodell<br />
bezieht. Sie stellt gewissermaßen eine Relation zwischen Modellen dieses<br />
Metamodells dar. Wir sprechen also weder von der Umsetzung eines Modells<br />
zwischen verschiedenen Abstraktionsebene noch von der Übertragung des Modells<br />
in <strong>and</strong>ere Metamodelle. Somit stellt für uns bereits die Umbenennung eines<br />
Modellelements eine Modelltransformation dar. Allerdings gilt unser Interesse<br />
hier wesentlich komplexeren Transformationen, die sich aus verschiedenen primitiven<br />
Einzelveränderungen des Modells zusammensetzen und eine Vielzahl<br />
von Modellelementen gleichzeitig verändern, sowie neue Elemente erzeugen und<br />
in das Modell einfügen.<br />
2.2 Das Modellierungswerkzeug AutoFocus 2<br />
AutoFocus 2 ist der Prototyp eines Modellierungswerkzeuges zur Entwicklung<br />
und Simulation eingebetteter Systeme, das am Lehrstuhl für <strong>Software</strong> & <strong>Systems</strong><br />
<strong>Engineering</strong> entwickelt und zu Wissenschaftszwecken eingesetzt wird. Es<br />
stellt eine Weiterentwicklung des AutoFocus-Werkzeugs ([<strong>TUM</strong>02]) dar. Wie<br />
die meisten modernen CASE-Werkzeuge 1 bietet es zur Modellierung graphische<br />
Editoren und verschiedene Sichten auf das modellierte System an.<br />
Wir werden zunächst einen Blick auf die für das später folgende Beispiel<br />
relevanten Modellierungssichten werfen und im zweiten Schritt das AutoFocus 2<br />
Metamodell (Beschreibung der Struktur von Modellen) beleuchten, da dieses<br />
besondere Bedeutung für die Transformationssprache ODL hat.<br />
2.2.1 Sichten und Editoren<br />
Die Modellierungssichten von AutoFocus 2 sind für die weitere Arbeit wichtig,<br />
da wir unsere Erläuterungen immer wieder an deren Aufteilung orientieren<br />
werden. Die konkreten Editoren und deren Benutzung sind dagegen kaum von<br />
Interesse. Der Leser sei hier auf die Dokumentation von AutoFocus und AutoFocus<br />
2 verwiesen ([<strong>TUM</strong>02], [<strong>TUM</strong>05]).<br />
Struktursicht<br />
Die Struktursicht beschreibt den Aufbau von eingebetteten Systemen in AutoFocus<br />
2. Systeme bestehen aus einzelnen Komponenten, die eigenständige<br />
1 CASE: computer-aided software engineering, computer-unterstützte <strong>Software</strong>entwicklung<br />
7
Recheneinheiten bzw. -funktionen darstellen. Sie können über ihre Schnittstelle,<br />
die sogenannten Ports, Nachrichten empfangen und versenden. Mehrere Komponenten<br />
können mitein<strong>and</strong>er über Kanäle verbunden werden und darüber kommunizieren.<br />
So entstehen aus Einzelkomponenten durch Komposition größere<br />
Systemteile und schließlich das Gesamtsystem.<br />
In der vorliegenden Arbeit konzentrieren wir uns allerdings auf einzelne, atomare<br />
Komponenten, d.h. Komponenten, die eine Verhaltensbeschreibung besitzen<br />
und selbst nicht wieder aus Subkomponenten bestehen.<br />
Verhaltenssicht<br />
In AutoFocus 2 wird das Verhalten von atomaren Komponenten durch Zust<strong>and</strong>smaschinen<br />
beschrieben. Die Komponente besteht aus einer Menge von<br />
Zuständen und Zust<strong>and</strong>sübergängen, den Transitionen, zwischen diesen Zuständen.<br />
Transitionen können dabei mit Vorbedingungen und Eingabemuster, sowie<br />
Ausgabemuster und Nachbedingungen (Aktionen) erweitert sein.<br />
Die Erfüllung der Vorbedingung und das Auftreten passender Eingangsnachrichten<br />
an den Eingabeports führt zu einem Zust<strong>and</strong>sübergang mit der Ausgabe<br />
der festgelegten Nachrichten auf den Ausgabeports und der Ausführung der Aktionen.<br />
Ausgabe nehmen Einfluß auf die Umgebung einer Komponente, wohingegen<br />
Aktionen lokale Variablen und somit den internen Komponentenzust<strong>and</strong><br />
verändern. Der Komponentenzust<strong>and</strong> ist durch den aktuellen Automatenzust<strong>and</strong><br />
und die Belegung der lokalen Variablen festgelegt.<br />
Bei Zuständen gibt es, wie bei Komponenten, ebenfalls einen hierarchische<br />
Struktur, d.h. ein Zust<strong>and</strong> kann wiederum Unterzustände, also einen Teilautomaten,<br />
enthalten. Im Beispiel kapseln wir die Anwendungsfunktionalität des<br />
Pressesteuermoduls in einem eigenen Zust<strong>and</strong>.<br />
Datensicht<br />
Benutzerdefinierte Datentypen werden über einen einfachen einem Texteditor<br />
ähnlichen Editor festgelegt. Die dazu verwendete Definitionssyntax betrachten<br />
wir in Kapitel 3. Aus der textuellen Repräsentation wird durch einen Parser das<br />
zugehörige Modell der Datendefinitionen erzeugt.<br />
Für unsere Betrachtungen ist die Datensicht zentral, da sie zum einen sehr<br />
komplexe Modellstrukturen aufweist und zum <strong>and</strong>eren wir hier den Entwickler<br />
möglichst weitgehend unterstützen wollen. In Kapitel 5 werden wir die Spezifikationsunifikation<br />
auf der Datensicht voll automatisieren.<br />
Weitere Sichten<br />
Das AutoFocus 2 Werkzeug besitzt noch eine Reihe weiterer Sichten, die für<br />
unsere Betrachtungen nicht von Bedeutung sind, der Vollständigkeit halber aber<br />
nicht unerwähnt bleiben sollen.<br />
Für die Definition von Beispielabläufen einzelner Komponenten stehen Sequenzdiagramme<br />
(Message Sequence Charts) zur Verfügung. Im AutoMode-<br />
Projekt wurden diese in Verbindung mit den Komponentenmodussicht zur Definition<br />
von datenflussorientiertem Verhalten benutzt (siehe hierzu [<strong>TUM</strong>04a]).<br />
Im AutoRaid-Projekt wurde AutoFocus 2 mit Modellierungselementen und<br />
entsprechenden Sichten bzw. Editoren für die Anforderungsdefinition ausgestattet<br />
(vgl. hierzu [<strong>TUM</strong>04b]).<br />
8
2.2.2 Das AutoFocus 2-Metamodell<br />
Gegenüber der ersten Version des AutoFocus-Werkzeugs, das die Datenspeicherung<br />
den Sichten entspreched vornahm, wurde die Architektur von AutoFocus 2<br />
so konstruiert, das sämtliche Modelldaten zusammen abgelegt werden. Zur Zeit<br />
geschieht dies in Form von XML-Dateien, konzeptuell ist die persistente Speicherung<br />
von Modellen aber auch in einer Datenbank möglich. Die Struktur der<br />
Daten, und somit der jeweiligen Speicherungsform, ist im Metamodell festgelegt.<br />
Das Metamodell beschreibt in Form von Klassendiagrammen, welche Modellelemente<br />
es gibt und wie diese mitein<strong>and</strong>er verknüpft sind bzw. sein können. Ein<br />
Metamodellelement beschreibt also eine ganze Klasse von Modellelementen.<br />
In den mit ODL formulierten Transformationen treten die Metamodellelemente<br />
als Bezeichner für Mengen, der jeweils im bearbeiteten Modell vorh<strong>and</strong>enen<br />
Elemente auf. So repräsentiert Component die Menge aller Komponenten<br />
innerhalb eines AutoFocus 2-Projekts. Zum Verständnis der Modelltransformationen<br />
und -analysen, die wir im Folgenden betrachten werden, ist die Kenntnis<br />
des Metamodells notwendig. Wir haben die relevanten Teile als Klassendiagramme<br />
dargestellt.<br />
Das Metamodell basiert selbst wieder auf einem Meta-Metamodell, was mit<br />
Blick auf die Portierbarkeit der Transformationssprache von Bedeutung ist. Diesen<br />
Aspekt beleuchten wir in Abschnitt 6.3 für die Anbindung von ODL an<br />
UML-Werkzeuge genauer.<br />
Wir wollen nun für die drei für uns relevanten Sichten die jeweiligen Metamodellteile<br />
betrachten. Dabei zeigen wir nur die für die folgenden Ausführungen<br />
relevanten Teile und lassen manche Spezialität, der besseren Verständlichkeit<br />
wegen, weg.<br />
Struktursicht<br />
Die Struktursicht beschreibt den Sytemaufbau und umfasst im Wesentlichen<br />
drei Klassen von Modellelementen. Wie Abbildung 2.1 zeigt sind dies die Metamodellelemente<br />
für Komponenten (Component), Schnittstellen (Port) und Kommunikationskanäle<br />
(Channel).<br />
SubComponents<br />
0..*<br />
Component<br />
Name: String<br />
Port<br />
Ports<br />
0..*<br />
Name: String<br />
DefinedType: MIFType<br />
Direction: Direction<br />
SuperComponent<br />
SourcePort<br />
DestinationPort<br />
OutChannels 0..*<br />
InChannel 0..1<br />
Channel<br />
Name: String<br />
DefinedType: MIFType<br />
Direction<br />
Channels<br />
0..*<br />
IsEntry: boolean<br />
Abbildung 2.1: Metamodell für Komponenten<br />
Die Klasse Direction kapselt die Information, ob ein Port-Element eine Ein-<br />
9
oder Ausgabeschnittstelle der Komponente darstellt. Sie wird ausserdem für<br />
die Richtung von Verbindungspunkten bei den Zuständen der Verhaltenssicht<br />
benötigt.<br />
Verhaltenssicht<br />
Die Zust<strong>and</strong>smaschinen zur Verhaltensbeschreibung finden sich im Metamodell<br />
als Automaton-Klasse wieder. Jede Komponente kann anstatt einer Subkomponentenstruktur<br />
mit einem Automaten verknüpft sein, wobei ein Automat als<br />
Verhaltensbeschreibung für mehrere Komponenten dienen kann.<br />
Ein Automat besteht aus einem Zust<strong>and</strong>, der das eigentliche Verhalten als<br />
Netz von Unterzuständen und Transitionssegmenten zwischen diesen enthält.<br />
Diese Unterzustände können ihrerseits wieder Zustände und Transitionen enthalten.<br />
Abbildung 2.2 zeigt den relevanten Ausschnitt des Metamodells.<br />
Automaton<br />
Name: String<br />
SubStates<br />
0..*<br />
State<br />
Name: String<br />
0..*<br />
InterfacePoint<br />
Name: String<br />
Direction: Direction<br />
SuperState<br />
SourcePoint<br />
DestinationPoint<br />
OutSegments 0..*<br />
InSegments<br />
0..*<br />
0..*<br />
TransitionSegment<br />
Name: String<br />
0..* 0..* 0..*<br />
Condition<br />
Input<br />
Output<br />
Action<br />
Port: MIFPort<br />
Pattern: MIFTerm<br />
Port: MIFPort<br />
Expression: MIFTerm<br />
Abbildung 2.2: Metamodell für Automaten<br />
Die InterfacePoint-Elemente haben im Zusammenhang mit hierarchischen<br />
Zuständen eine weitergehende Bedeutung, weil ein hierarchischer Zust<strong>and</strong> eine<br />
Aussen- und eine Innenansicht hat. Sie stellen die Verbindungspunkte für Transitionssegmente<br />
dar. Durch diese Elemente ist dabei möglich Transitionen über die<br />
Hierarchieebenen hinweg zu definieren, indem Transitionssegmente über diese<br />
Verbindungsstellen gekoppelt werden. Wir verwenden in unserem Beispiel zwar<br />
einen Unterzust<strong>and</strong> für die Pressesteuerung, wollen die Verbindungspunkte für<br />
unsere Betrachtungen aber zunächst als einfache Zwischenelementen von Zust<strong>and</strong><br />
und Transitionssegment ansehen. Im Programm in Anhang A, in dem wir<br />
10
auch den Unterautomaten kopieren, werden wir sie dann voll berücksichtigen.<br />
Ein Transitionssegment kann eine Vorbedingung (Condition) und mehrere<br />
Aktionen (Action) besitzen, die wir im Beispiel aber nicht weiter betrachten werden,<br />
sondern erst im Anhang A. Für die Spezifikationsvereinigung unterscheiden<br />
sich diese beiden nicht wesentlich von den Ein- und Ausgangsnachrichten (Input<br />
und Output), bringen uns also nichts konzeptuell Neues.<br />
Die Metamodellklassen MIFPort und MIFTerm folgen im nächsten Abschnitt.<br />
Datensicht<br />
Datentypen Mit Datentypen und deren Definition beschäftigen wir uns ausführlich<br />
in Kapitel 3. Wir belassen es hier bei der dem Hinweis, dass die Struktur<br />
und der Aufbau von Typdefinitionen in Abschnitt 3.1 im Zusammenhang mit der<br />
Typunifikation genau erläutert wird und wenden uns hier den mit Datentypen<br />
eng in Verbindung stehenden Termelementen zu.<br />
Terme Für unser Unifikationsvorhaben sind die Terme der Ein- bzw. Ausgabeausdrücke<br />
von Transitionen von besonderem Interesse. In Abschnitt 5.4.5<br />
werden wir mit ihnen eine Strukturunifikation durchführen. Daher wollen wir<br />
uns hier noch den Ausschnitt des Metamodells ansehen. Die Metamodellelemente<br />
Constructor und Selector werden ebenfalls im Kapitel 3 genau eingeführt.<br />
Args 1..* <br />
Term<br />
Appl<br />
Head<br />
Const<br />
Name: String<br />
Constructor<br />
Selector<br />
Abbildung 2.3: Metamodell für Terme<br />
Abbildung 2.3 zeigt, dass ein Term entweder ein Konstruktor oder eine<br />
Funktionsapplikation (Appl) sein kann. Letztere besteht aus einer angew<strong>and</strong>ten<br />
Funktion (Head-Assoziation) und einer geordneten Menge von Parametertermen<br />
(Args-Assoziation). Syntaktisch ist es daher auch möglich, Selektorfunktionen<br />
als angew<strong>and</strong>te Funktion in Termen zu verwenden. Da dies aber unser Vereinigungsproblem<br />
nur um gleichartige Fälle ausdehnt, werden wir Terme auf die<br />
Anwendung von Konstruktoren beschränken (siehe Abschnitt 2.5).<br />
Die Konstruktion von zulässigen Termen übernimmt in AutoFocus 2 ein<br />
entsprechender Parser. Aus ODL heraus ist ein Zugriff auf diese Funktion allerdings<br />
nicht möglich, weshalb wir selbst die Konstruktion übernehmen müssen.<br />
Wir haben uns dabei aber auf den für Beispiel relevanten Fälle beschränkt und<br />
keine Unifikation für alle in AutoFocus 2 möglichen Terme implementiert.<br />
11
Modellschnittstellen Datentypen, Terme und Ports sind manchmal über eine<br />
weitere Abstraktionsschicht, der Modellschnittstelle (Model InterFace, MIF)<br />
mit den übrigen Modellelementen verknüpft. Abbildung 2.4 zeigt die entsprechenden<br />
Metamodellelemente. Durch diese Abstraktion ist es möglich unvollständige<br />
AutoFocus 2-Modelle zu erstellen, d.h. zum Beispiel Modelle die Ports<br />
enthalten, deren Datentypen noch nicht definiert sind. Für das MIF-Element<br />
wäre in diesem Falle die Model-Assoziation nicht gesetzt, sondern nur das Text-<br />
Attribut belegt.<br />
MIFType<br />
text: String<br />
MIFTerm<br />
text: String<br />
MIFPort<br />
text: String<br />
Model<br />
Model<br />
Model<br />
Type<br />
Term<br />
Port<br />
Abbildung 2.4: Metamodell für Modellschnittstellen<br />
Bei unseren Betrachtungen werden wir diese Möglichkeit nicht weiter beachten.<br />
Wir gehen von vollständigen Modellen aus, d.h. alle MIF-Elemente besitzen<br />
ein entsprechende Model-Repräsentation.<br />
2.3 Die Transformationssprache Operation Definition<br />
Language<br />
Die Transformationssprache Operation Definition Language (ODL) ist in [Sch01]<br />
definiert und durch mehrere Studentenarbeiten in AutoFocus/Quest und AutoFocus<br />
2 implementiert worden. Sie erlaubt ähnlich, wie die Object Constraint<br />
Language (OCL, [OMG03b]), die Definition von Modellnebenbedingungen<br />
(Constraints), aber auch von Modelltransformationen. Besonderheit der ODL ist<br />
dabei die Integration einer Nutzerschnittstelle, sodass Analysen und Transformationen<br />
interaktiv durchgeführt werden können.<br />
2.3.1 Grundlagen<br />
Die Operation Definition Langauge ist ein typbasierter Relationenkalkül mit<br />
den Mengen der Modellelemente eines Metamodells als Typen und den Assoziationen<br />
und Attributen als Relationen. Sie ähnelt, in ihrer Anlehnung an die<br />
Prädikatenlogik erster Stufe, der aus dem Bereich der Datenbanken bekannten<br />
Structured Query Language.<br />
Die Auswertung von ODL Ausdrücken geschieht immer in Verbindung mit einer<br />
Metamodellinstanz, also mit einem konkreten Modell. Die Konzeption stellt<br />
dabei sicher, dass alle Ausdrücke garantiert terminieren. Ein eigenes Typsystem<br />
erlaubt aus den Modellelementmengen komplexere Typen, wie beispielsweise<br />
Produkttypen, aufzubauen. Dieses Typsystem wird in [Höl05] genau erläutert.<br />
12
ODL Ausdrücke stellen, analog zur Prädikatenlogik erster Stufe, Formeln mit<br />
Quantoren dar. Neben den Existenz- und Allquantoren besitzt sie zusätzlichen<br />
einen Interaktionsquantor (context), der die Variablenbindung nicht iterativ<br />
vornimmt, sondern dem Benutzer die iterierte Menge in einem Dialog anzeigt<br />
und eine Auswahl verlangt.<br />
Neben dem Analysieren von Modellen und Überprüfen von Nebenbedingungen<br />
ist eine wichtige Fähigkeit der ODL neue Modellelemente und Verknüpfungen<br />
zwischen Modellelementen schaffen zu können. Wir beschäftigen uns damit<br />
ausführlich im Abschnitt 5.5.<br />
2.3.2 Beispiele<br />
Einige kurze Beispiele sollen hier einen Eindruck der ODL vermitteln.<br />
Überprüfung aller Porttypen auf Definiertheit<br />
Wir wollen Überprüfen, ob für alle Port-Elemente entsprechende Type-Elemente<br />
existieren. Wir betrachten also für jedes Schnittstellenelement, ob das zugehörige<br />
Modellschnittstellenelement (MIFType) mit einem Type-Element verknüpft ist.<br />
forall p:Port . exists t:Type . p.DefinedType.Model = t<br />
Interaktives Umbenennen einer Komponente<br />
Der Nutzer wird um die Eingabe einer Komponente und eines Strings gebeten,<br />
worauf diese Komponente entsprechend umbenannt wird.<br />
context tupel:(comp:Component, name:String) .<br />
result has Name(tupel.comp, tupel.name)<br />
Erzeugen eines neuen Ports<br />
In diesem Beispiel erzeugen wir ein neues Port-Element und fügen es der vom<br />
Benutzer gewählten Komponente hinzu. Wir setzen auch die Richtung des Ports<br />
und seinen Typ, indem wir die entsprechenden Kapselelemente generieren und<br />
mit den Nutzereingaben verknüpfen. Als Porttyp erlauben wir nur Typkonstanten<br />
(TConst).<br />
context data:(c:Component, s:String, dir:Boolean) .<br />
context dtd:DTDModule .<br />
context t:(dtd.TConsts) .<br />
exists np:new Port . (<br />
exists nd:new Direction . (<br />
result has IsEntry(nd, data.dir) <strong>and</strong><br />
result has Direction(np, nd)<br />
) <strong>and</strong><br />
exists nmt:new MIFType . (<br />
result has DefinedType(np, nmt) <strong>and</strong><br />
result has Text(nmt, t.Name) <strong>and</strong><br />
result has Model(nmt, t)<br />
) <strong>and</strong><br />
13
)<br />
result has Name(np, data.s) <strong>and</strong><br />
result has Ports(data.c, np)<br />
2.4 Modelltransformation: AutoFocus 2 Spezifikationsvereinigung<br />
Zum Aufbau größerer Modelle wird meist Komposition eingesetzt, d.h. eine<br />
größere Komponente besteht ihrerseits aus einer Anzahl von mitein<strong>and</strong>er kooperierender<br />
und kommunizierender Subkomponenten. Hin und wieder kann es<br />
aber notwendig sein, eine Komponente als Verschmelzung von zwei oder mehr<br />
bereits spezifizierten Komponenten zu konstruieren. Insbesondere die Kombination<br />
von anwendungsspezifischen Spezifikationen mit generellen, möglicherweise<br />
systemweiten, Spezifikationen ist hier von Interesse. Bisher ist die Durchführung<br />
solcher Verschmelzungen auf Spezifikations- und Modellebene wenig untersucht.<br />
Gängige Praxis ist jedoch die Zusammenführung auf Codeebene durch die Entwickler,<br />
was zeitaufwändig und fehleranfällig ist.<br />
In dieser Arbeit entwickeln wir eine Modelltransformation mit ODL, die für<br />
die Vereinigung von einfachen AutoFocus 2-Komponenten eingesetzt werden<br />
kann. Wir wollen zum Abschluß dieses Kapitels unser Vorhaben anh<strong>and</strong> eines<br />
konkreten Modells exemplarisch vorführen. Dabei beschreiben wir die Vereinigung<br />
zweier Komponenten aus den drei, hierfür wesentlichen, schon bekannten<br />
AutoFocus 2-Sichten: der Struktursicht, der Verhaltenssicht und der Datensicht.<br />
2.4.1 Struktursicht<br />
Als Beispiel betrachten wir die Komponente Press, die die Funktionalität einer<br />
Presse modelliert. Wir wollen sie mit der Komponente ErrorTreatment, die ein<br />
allgemeines Fehlerverhalten modelliert, verschmelzen. Abbildung 2.5 zeigt aus<br />
Sicht der <strong>Systems</strong>truktur diese beiden Komponenten, sowie das Ergebnis der<br />
Vereinigung, die Komponente MergedController.<br />
Cntrl : Control<br />
ErrorTreatment<br />
Sts : Status<br />
Cmd : Comm<strong>and</strong><br />
CntrlCmd : ControlComm<strong>and</strong><br />
SnsUp : SensorValue<br />
Press<br />
SnsUp : SensorValue<br />
MergedController<br />
Sts : Status<br />
SnsDn : SensorValue<br />
Act : ActorValue<br />
SnsDn : SensorValue<br />
SnsPc : SensorValue<br />
SnsPc : SensorValue<br />
Act : ActorValue<br />
Abbildung 2.5: Spezifikationsvereinigung der Struktursicht<br />
Die Komponente für das Fehlerverhalten besitzt einen Eingabeport Cntrl<br />
zur Steuerung und einen Ausgabeport Sts für Statusnachrichten.<br />
Die Komponente für die Pressefunktionalität hat neben dem Steuereingang<br />
14
(Cmd), drei Signalleitungen zu Sensoren und eine Signalleitung zu einem Aktuator.<br />
Sie haben die folgenden Bedeutungen:<br />
• SnsUp - Sensorsignal, das anzeigt, dass die Presse gehoben ist.<br />
• SnsDn - Sensorsignal, das anzeigt, dass die Presse gesenkt ist.<br />
• SnsPc - Sensorsignal, das anzeigt, ob sich ein Werkstück in der Presse<br />
befindet.<br />
• Act - Aktuatorsignal, zum Ansteuern des Motors, der die Presse bewegt.<br />
Die vereinigte Komponente MergedController hat ebenfalls die drei Sensorports,<br />
den Status- und den Aktuatorport. Sie wurden als Kopie von den beiden<br />
Quellkomponenten übernommen. Die Besonderheit ist allerdings der neue<br />
Steuerport CntrlCmd, der die Vereinigung der beiden Steuerports Cntrl von<br />
ErrorTreatment und Cmd von Press darstellt. Insbesondere muss für diesen neuen<br />
Port auch der kombinierte Typ ControlComm<strong>and</strong> generiert werden (siehe unten).<br />
2.4.2 Verhaltenssicht<br />
Die Abbildung 2.6 zeigt die Automaten der beiden Quellkomponenten und der<br />
verschmolzenen Komponente. Das dargestellte Ergebnis ist nur eines von mehreren<br />
syntaktisch korrekten Ergebnissen. Wir haben hier die sinnvolle Variante<br />
gewählt, die möglichst viele Elemente vereinigt.<br />
Es werden die Zust<strong>and</strong>spaare (Operate, Operate) und (Stop, Off) vereingt.<br />
Die Benennung der neuen Zustände erfolgt durch Konkatenieren der Quellzust<strong>and</strong>snamen.<br />
Die Transitionen werden entsprechend dem Verlauf in der Abbildung unifiziert.<br />
Besonders sei angemerkt, dass die Vereinigung der beiden Transitionen<br />
von Stop nach Operate und von Off nach Operate erzwingt, dass das Eingabesignal<br />
für die Transition zwischen Init und Operate ebenfalls angepasst wird.<br />
Siehe hierzu auch die unten gezeigte Vereinigung der Typen.<br />
Die mit (D) gekennzeichneten Zustände zeigen an, dass diese Zustände Unterautomaten<br />
bzw. Unterzustände besitzen. In unserem Beispiel ist die Steuerungslogik<br />
der Presse hier als Unterautomat gekapselt. Abbildung 2.7 zeigt diese<br />
Steuerung.<br />
Nach dem Start wird die Presse zunächst gehoben (Zust<strong>and</strong> Up). Dies geschieht<br />
für maximal 100 Zeitschritte oder bis der Sensor SnsUp das vollständige<br />
Heben anzeigt. Danach wartet die Presse auf ein Werkstück (Zust<strong>and</strong> Wait).<br />
Sobald das Vorh<strong>and</strong>ensein eines Werkstücks durch den Sensor SnsPc erkannt<br />
wurde, wird die Presse gesenkt (Zust<strong>and</strong> Down). Auch dies geschieht wieder<br />
für 100 Zeiteinheiten oder bis die volle Senkung erreicht wurde. Für die Zust<strong>and</strong>sübergänge,<br />
die die Presse in Bewegung setzen, sind Sensorsignale absolut<br />
notwendig. Wir wollen nicht, dass Zust<strong>and</strong>sübergänge aufgrund von Zeitbedingungen<br />
geschehen. Sollte nach 100 Zeiteinheiten kein Signal eingetroffen sein, so<br />
bleibt die Steuerung im entsprechenden Zust<strong>and</strong> und muss abgeschaltet werden.<br />
Man sieht an diesem Fall, das die Vereinigung dieser Steuerungslogik mit der<br />
Fehlerbeh<strong>and</strong>lungslogik praktische Bedeutung hat. Auch wenn die verschmolzene<br />
Anwendungslogik noch unvollständig ist und angepasst werden muss, können<br />
15
ErrorTreatment Verhalten<br />
:Cntrl?Start:<br />
Sts!Started:<br />
Operate<br />
Press Verhalten<br />
Operate<br />
Init<br />
:Cntrl?Stop:<br />
Sts!Stopped:<br />
::Sts!Error:<br />
errCode=1<br />
errCode=0:Cntrl?Start:Sts!Started:<br />
D<br />
:Cmd!Off::<br />
:Cntrl?Reset:<br />
Sts!Initial:<br />
errCode=0<br />
Stop<br />
Off<br />
MergedController Verhalten<br />
:CntrlCmd?StartOn:<br />
Sts!Started:<br />
Operate<br />
Operate<br />
D<br />
Init<br />
:CntrlCmd?StopOff:<br />
Sts!Stopped:<br />
::Sts!Error:<br />
errCode=1<br />
errCode=0:<br />
CntrlCmd!StartOn:<br />
Sts!Started:<br />
:CntrlCmd?Reset:<br />
Sts!Initial:<br />
errCode=0<br />
StopOff<br />
:Cmd!On::<br />
Abbildung 2.6: Spezifikationsvereinigung der Verhaltenssicht<br />
Press.Operate Verhalten<br />
Wait<br />
:SnsPc?Signal:<br />
Act!Lower:<br />
timer = 0<br />
:Cmd!Off::<br />
timer
doch schon viele Anpassungen im Modell konsistent und automatisch durchgeführt<br />
werden. Der Entwickler muss nur noch die Ergebniskomponente vervollständigen.<br />
Für die Vereinigung definieren wir, dass Zustände nur auf der obersten Ebene<br />
eines hierarchischen Automaten unifiziert werden können. Unterautomaten<br />
werden nur einkopiert. Daher darf nur einer der jeweils mitein<strong>and</strong>er unifizierten<br />
Zustände Unterzustände haben, wie das für unser Beispiel der Fall ist. Das Kopieren<br />
werden wir jedoch nur für die erste Unterebene ausprogrammieren, da es<br />
für die vollständige Hierarchie im Wesentlichen dem Kopieren einer Komponentenhierarchie,<br />
wie in [Höl05], entspricht.<br />
Wir können durch zusätzliche vorgeschaltete Transformationen die hierarchische<br />
Struktur eines Automaten verändern, d.h. sozusagen Zustände in einen<br />
hierarchischen Zust<strong>and</strong> verpacken bzw. sie daraus entpacken. Somit lässt die<br />
Spezifikationsvereinigung leicht auf zusätzliche Anwendungsfälle erweitern, indem<br />
wir diese Verhaltensbeschreibungen auf die hier verlangte Form bringen.<br />
Siehe hierzu auch die Ausführungen über alternative Unifikationsrelationen in<br />
Abschnitt 4.2.<br />
2.4.3 Datensicht<br />
In Kapitel 3 untersuchen die Datentypdefinitionen, insbesondere die Datenmodellierung<br />
nach dem Konstruktor-Selektorprinzip. Hier wollen wir nur die zu<br />
unserem Beispiel gehörenden Datentypen auflisten.<br />
// ErrorTreatment Datentypen //<br />
data Control = Start | Stop | Reset;<br />
data Status = Initial | Started | Stopped | Error;<br />
// Press Datentypen //<br />
data Comm<strong>and</strong> = On | Off;<br />
data SensorValue = Signal;<br />
data ActorValue = Raise | Lower;<br />
// MergedController Datentypen //<br />
data ControlComm<strong>and</strong> = StartOn | StopOff | Reset;<br />
Abbildung 2.8: Spezifikationsvereinigung der Datensicht<br />
Abbildung 2.8 zeigt die Datentypen der beiden Quellkomponenten Error-<br />
Treatment und Press, sowie die neu hinzugekommenen Ergebnisdatentypen nach<br />
der Vereinigung, MergedController. Bei unserem einfachen Beispiel haben wir<br />
auf die Verwendugn von Selektoren verzichtet. Diese werden wir jedoch im Folgenden<br />
sehr wohl berücksichtigen, da sie einige Besonderheiten im Hinblick auf<br />
die Unifikation erfordern.<br />
Die Namen der neuen Datendefinition und der Konstruktoren ergeben sich<br />
wieder durch Konkatenation der Quellelementnamen.<br />
17
2.5 Einschränkungen für die Modellierungselemente<br />
AutoFocus 2 bietet eine breite Palette von Modellierungsmöglichkeiten für komponentenbasierte<br />
Systeme. In dem dargestellten Beispiel haben wir auf einige<br />
dieser Möglichkeiten verzichtet, da diese eine Vielzahl von Sonderfällen erzeugen.<br />
Unser Anspruch ist hier nicht den perfekten Ablauf der Spezifikationsvereinigung<br />
zu präsentieren, sondern das Grundprinzip zu verdeutlichen und<br />
zu zeigen, dass solch komplexe Modelltransformationen durchführbar sind. Eine<br />
einsatzfähige Transformation muss im Kontext eines Entwicklungsprozesses<br />
massgeschneidert werden. Wir legen hier den Fokus auf die theoretische Fundierung<br />
sowie die technische Umsetzung als Konzeptbeweis.<br />
Im Einzelnen haben wir folgende Einschränkungen benutzt:<br />
• Die zu vereinigenden Komponenten besitzen keine hierarchische Struktur.<br />
Wir verwenden nur Einzelkomponenten zur Vereinigung, deren Verhalten<br />
durch einen Automaten beschrieben ist.<br />
• Die Unifikation von lokalen Variablen führen nur im Programmlisting in<br />
Anhang A durch.<br />
• Die Vereingung der Automaten ist flach, d.h. wir gehen davon aus, dass die<br />
Automaten keine hierarchischen Zustände besitzen. Für solche Zustände<br />
müsste zusätzlicher Aufw<strong>and</strong> betrieben werden, um die Struktur rekursiv<br />
abzuarbeiten. Unterautomaten werden erst im Programm miteinbezogen,<br />
dort jedoch auch nur kopiert, nicht unifiziert.<br />
• Automaten haben keine ausgezeichneten Zustände, wie Start- und Endzustände.<br />
• Die Zust<strong>and</strong>sübergänge besitzen keine Vorbedingungen und keine Aktionen,<br />
da hierzu zunächst die Definition entsprechender Terminterpretationen<br />
notwendig ist, um über die Vereinigung von Vorbedingungen zu sprechen.<br />
• Zahlreiche Modellattribute wurden der Übersichtlichkeit wegen weggelassen,<br />
insbesondere Comment und Ähnliches.<br />
• Ausschließliche Verwendung von nutzerdefinierten Datentypen; alle Werte<br />
sind als Konstruktoren vorh<strong>and</strong>en. Wir verzichten auch auf den Einsatz<br />
primitiver Datentypen, da sie nur zusätzliche zu berücksichtigende<br />
Fälle erzeugen, die keine neuen Konzepte mit sich bringen und somit dem<br />
Verständnis nur abträglich sind.<br />
• Funktionsapplikationen in Termen müssen als Funktion einen Konstruktor<br />
haben. Selektoren sind nicht zugelassen. Die Begründung ist die selbe wie<br />
eben.<br />
• Polymorphe Datentypen sind nicht Teil unserer Betrachtungen, da sie eine<br />
zusätzliche Abstraktionsebene einführen, die eigene Vereinigungsverfahren<br />
erfordert.<br />
18
• Viele Modellelemente müssen bezüglich eines Attributs (meist Name) im<br />
gesamten Modell eindeutig sein. Diese Auflage überprüfen wir bei der<br />
Konstruktion nicht, da sie jeweils sehr leicht durch Iteration über alle<br />
vorh<strong>and</strong>en Elemente und bei Namensgleichheit durch Nutzerinteraktion<br />
behoben werden kann. Wir verwenden der Einfachheit halber mittels der<br />
ODL-Funktion concat zusammengesetzte Namen.<br />
2.6 Zusammenfassung<br />
In diesem Kapitel haben wir uns dem Thema Modelltransformation gewidmet.<br />
Nach der Begriffsklärung haben wir die Grundlagen des Modellierungswerkzeuges<br />
AutoFocus 2 dargestellt. Anh<strong>and</strong> der Modellierungssichten haben wir<br />
die Datenarchitektur von AutoFocus 2-Modellen, das Metamodell, betrachtet.<br />
Dieses Wissen ist die notwendige Grundlage für das Verständnis der Transformationssprache<br />
ODL, welche wir anh<strong>and</strong> einfacher Beispiele eingeführt haben.<br />
Nachdem wir nun sowohl das Modellierungswerkzeug als auch die Implementierungssprache<br />
für die Modelltransformationen betrachtet haben, gingen wir<br />
zur exemplarischen Erläuterung der Modelltransformation Spezifikationsunifikation<br />
über. Für die weiteren Ausführungen dieser Arbeit haben wir abschließend<br />
die Beschränkungen für Modelle zur Nutzung unserer Transformation erklärt.<br />
Das Programmlisting im Anhang A wird einige dieser Beschränkungen wieder<br />
aufheben. Zur Erläuterung des Konzeptes ist diese Aufhebung jedoch nicht notwendig,<br />
da keine wesentlichen neuen Aspekte auftreten. Das Grundprinzip der<br />
Unifikation ist entscheidend, nicht wie viele Metamodellelemente in sie miteinbezogen<br />
werden. Deshalb werden wir im nächsten Kapitel die Unifikation genau<br />
erläutern.<br />
19
Kapitel 3<br />
Einführungsbeispiel:<br />
Typunifikation<br />
Bevor wir uns der Lösung des Spezifikationsvereinigung zuwenden werden, wollen<br />
wir an einem Teilbeispiel, der Typdefinitionsvereinigung, das Grundprinzip<br />
aufzeigen. Wir werfen zunächst einen genaueren Blick auf die Modellierungsmöglichkeiten<br />
von AutoFocus 2 im Bereich Datentypen. Insbesondere werden<br />
wir den Zusammenhang zwischen modellierten Datentypen und dem Metamodell<br />
beleuchten. Im folgenden Teil werden wir das aus der Prädikatenlogik bekannte<br />
Verfahren der Unifikation auf die Typvereinigung übertragen. Die beiden<br />
letzten Abschnitte dieses Kapitels beschäftigen sich mit der Umsetzung in ODL.<br />
Zunächst beschränken wir uns auf die prädikative Form, d.h. wir überprüfen zwei<br />
Datentypdefinitionen auf Vereinbarkeit. Danach gehen wir über zu einer interaktiven<br />
ODL-Abfrage, die es dem Nutzer erlaubt seine Vereinigungswünsche<br />
direkt anzugeben.<br />
3.1 AutoFocus 2 Datentypen und Metamodell<br />
3.1.1 Benutzerdefinierte Datentypen<br />
In AutoFocus 2 werden benutzerdefinierte Typen nach einem einfachen Konstruktor-Selektor-Prinzip<br />
festgelegt. Ein Datentyp besteht aus einer Liste von<br />
Konstruktorfunktionen. Diese besitzen beliebig viele Parameter, die Selektoren.<br />
Ein einfacher Datentyp MySignal, der ein Signal modelliert, dass entweder<br />
abwesend (Absent) ist oder bei Vorh<strong>and</strong>ensein (Present) einen Ganzzahlwert<br />
überträgt, wird folgendermaßen definiert:<br />
data MySignal = Absent | Present( value:Int );<br />
Der im Beispiel verwendete Typ Int gehört zu einer Reihe von Grundtypen,<br />
die AutoFocus 2 vorgibt, und die keine Datendefinition besitzen. Diese<br />
Grundtypen sind jedoch von besonderer Bedeutung, da sie die Fundierung des<br />
rekursiven Verfahrens, wie unten beschrieben, bilden. Abbildung 3.1 zeigt das<br />
zugehörige Objektdiagramm, das für AutoFocus 2 aus obiger Festlegung entsteht.<br />
Links finden sich die Definitionsobjekte für die Typdefinition (DataDef),<br />
20
die Konstruktoren (Constructor) und den Selektor (Selector). Die Objekte in<br />
der rechten Hälfte bilden die interne Typstruktur des Selektors.<br />
:DataDef<br />
:AbstractType<br />
:AbstractType<br />
Name = "MySignal"<br />
TConst<br />
Name = "MySignal"<br />
Name = "Int"<br />
TArgs [0]<br />
TArgs [1]<br />
Constructors [0] Constructors [1]<br />
:Constructor<br />
Name = "Absent"<br />
:Constructor<br />
Name = "Present"<br />
Selectors [0]<br />
:Selector<br />
Type<br />
:TAppl<br />
THead<br />
:AbstractType<br />
Name = "value"<br />
Name = "->"<br />
Abbildung 3.1: Objektdiagramm einer einfachen Typdefinition<br />
Dieser Typstruktur von Selektoren müssen wir besondere Aufmerksamkeit<br />
widmen. Selektoren stellen im eigentlichen Sinne Funktionen dar, die aus einer<br />
Instanz des Datentyps die entsprechende Teilinformation extrahiert. Der im Beispiel<br />
verwendet Selektor value hat demzufolge nicht den Typ Int, wie Definitionssyntax<br />
nahelegt, sondern den Funktionsapplikationstyp MySignal -> Int. Im<br />
Objektdiagramm findet sich dieser Typ als TAppl-Objekt mit der ->-Funktion als<br />
angew<strong>and</strong>te Funktion (THead) und den entsprechenden AbstractType-Objekten<br />
als Parameter (TArgs) wieder.<br />
Abbildung 3.2 zeigt den hier wesentlichen Ausschnitt aus dem AutoFocus 2-<br />
Metamodell. Neben den organisatorischen Klassen, DTD und DTDModule, finden<br />
sich sämtliche im Objektdiagramm der Datentypdefinition vorkommenden Klassen.<br />
Die Oberklassen sind nur der Vollständigkeit halber angegeben. ODL musste<br />
im Verlauf dieser Diplomarbeit erweitert werden, um mit Vererbungsbeziehungen<br />
im Metamodell korrekt umgehen zu können.<br />
3.1.2 Sonderbeh<strong>and</strong>lung von Selektortypen<br />
ODL ist bisher nur auf ungeordneten Mengen definiert und kann daher im Prinzip<br />
mit Assoziationen, die im Metamodell den Stereotyp haben,<br />
nicht mehr anfangen als mit herkömmlichen Assoziationen. Allerdings muss bei<br />
den Typen der Selektoren die Argumentliste korrekt interpretiert werden. Für<br />
einen Typvergleich müssen wir aus der Argumentliste der Funktionsapplikation<br />
den Ergebnistyp, also das zweite Argument der geordneten Relation, extrahieren.<br />
Hierfür wurde ODL um die Funktion indexOf(listElem, baseElem,<br />
relName) erweitert. listElem ist das assoziierte Element, dessen Index wir bestimmen<br />
wollen, baseElem das Ausgangselement und relName der Name der zu<br />
durchsuchenden Relation. Diese Erweiterung, die sich auf das in [Höl05] eingeführte<br />
Funktionsframework stützt, war die einfachste Alternative den Spezialfall<br />
geordneter Assoziationen zu h<strong>and</strong>haben.<br />
Um die Überprüfung eines Selectors auf einen bestimmten Ergebnistyp zu<br />
erleichtern, verwenden wir das folgende benamte Prädikat. Es nimmt mit Hilfe<br />
21
DTD<br />
0..*<br />
DTDModule<br />
0..*<br />
TypeDef<br />
0..*<br />
Type<br />
Type<br />
Term<br />
Name: String<br />
TArgs 1..* <br />
0..*<br />
TConst<br />
TConst<br />
Name: String<br />
THead<br />
TAppl<br />
Const<br />
Name: String<br />
AbstractType<br />
Constructors<br />
Selectors<br />
DataDef Constructor Selector<br />
1..*<br />
0..*<br />
<br />
<br />
Abbildung 3.2: AutoFocus 2 Metamodell: Datentypdefinitionen<br />
der ODL-Funktion indexOf die Extraktion des Ergebnistyps, also des zweiten<br />
TArgs-Parameter mit Index 1, der Funktionsapplikation vor und vergleicht dann<br />
diesen mit dem Parameter t des Prädikats.<br />
define testSelectorType (<br />
sel:Selector,<br />
t:Type<br />
) := (<br />
/* find the functionapplication */<br />
exists ta:TAppl . (<br />
sel.Type = ta <strong>and</strong><br />
exists ta_arg:(ta.TArgs) . (<br />
/* find the second argument (its index is 1) */<br />
indexOf(ta_arg, ta, "TArgs") = 1 <strong>and</strong><br />
ta_arg = t<br />
))) .<br />
Die Verwendung des benamten Prädikats ist sinnvoll, da zum einen die ODL-<br />
Ausdrücke im folgenden besser lesbar und strukturiert sind; zum <strong>and</strong>eren benötigen<br />
wir die Überprüfung des Selektortyps an mehreren Stellen innerhalb eines<br />
Ausdrucks, wodurch sich eine kompaktere Form ergibt. Sei s eine ODL-Variable<br />
vom Typ Selector und t eine Variable vom Typ Type, können wir nun obiges<br />
Prädikat mit<br />
call testSelectorType(s, t)<br />
22
aufrufen und müssen nicht jeweils den kompletten Prädikatsausdruck an den<br />
entsprechenden Stellen einfügen.<br />
3.1.3 Rekursive und polymorphe Typen<br />
AutoFocus 2 erlaubt es auch rekursive Typen, wie Listen, zu definieren. Ein<br />
Anwendungsbeispiel hierfür sind die folgenden beiden Listen:<br />
IntList = Empty | Append(i:Int, rest:IntList)<br />
AnotherList = Empty | Append(i:Int, rest:AnotherList);<br />
Rekursive Typen unterscheiden sich nur geringfügig bei der Konstruktion der<br />
Fixpunktmenge. In Abschnitt 3.3, wenn wir das Vereinbarkeitsprädikat definieren,<br />
weisen wir an entsprechender Stelle darauf hin.<br />
Das Metamodell lässt bei Typfestlegungen ebenfalls polymorphe Typen zu.<br />
Ein Beispiel hierfür ist der Typ des polymorphen Baumes:<br />
T ree α = EmptyT ree | Grow(left : T ree α, value : α, right : T ree α);<br />
Diese AutoFocus 2-Spezifikationsmöglichkeit verfolgen wir an dieser Stelle nicht<br />
weiter, da hierzu kompliziertere Mechanismen festgelegt werden müssen, wie<br />
die Instanziierung polymorpher Typen und die Unifikation bei Typen höherer<br />
Ordnung.<br />
3.2 Unifikation und Typvereinbarkeit<br />
Die Unifikation sollte dem Leser beispielsweise aus [Sch00] oder <strong>and</strong>eren Quellen<br />
über Lösungsverfahren der Prädikatenlogik bekannt sein. Hier soll nur das<br />
Grundprinzip aufgezeigt werden, um dann den Zusammenhang der Typvereinbarkeit<br />
mit der Unifikation verdeutlichen zu können.<br />
3.2.1 Unifikation in der Prädikatenlogik<br />
Zwei prädikatenlogische Formeln F 1 und F 2 zu unifizieren bedeutet, sie durch eine<br />
geeignete Ersetzung (Substitution) σ der freien Bezeichner auf eine identische<br />
Form zu bringen.<br />
Sei P ein Prädikat, f eine Funktion, x, y Variablenbezeichner, a, b Konstanten<br />
so können<br />
mittels einer der Substitutionen<br />
F 1 = P (f(x))<br />
F 2 = P (y)<br />
σ 1 = [f(x)/y]<br />
σ 2 = [f(a)/y, a/x]<br />
unifiziert werden. Hierbei ist σ 1 der allgemeinste Unifikator.<br />
Der zugehörige Unifikationsalgorithmus untersucht die beiden Formeln Zeichen<br />
für Zeichen bis er auf unterschiedliche trifft. Es kann dann ein Teil des<br />
allgemeinsten Unifikators ermittelt werden, vorausgesetzt dass folgende beiden<br />
Bedingungen erfüllt sind:<br />
23
1. eines der beiden Zeichen ist ein Variablenbezeichner<br />
2. sei x dieser Bezeichner und t der Term in der zweiten Formel, so kommt<br />
x nicht in t vor.<br />
Daher ist weder P (a) mit P (b) noch P (x) und P (f(x)) mitein<strong>and</strong>er unifizierbar.<br />
Wir werden gleich sehen, dass sich ähnliche Fälle auch bei der Typvereinbarkeit<br />
ergeben.<br />
3.2.2 Typunifikation in AutoFocus 2<br />
Wir definieren nun die Typvereinbarkeit über dem AutoFocus 2-Metamodell,<br />
wie es eben vorgestellt wurde.<br />
Zwei Typen T 1 und T 2 sind vereinbar, wenn<br />
1. T 1 = T 2 , oder<br />
2. für alle namensgleichen Konstruktoren und darin enthaltenen namensgleichen<br />
Selektoren gilt, dass die Selektortypen vereinbar sind.<br />
Zwei Typen sind also vereinbar, wenn sie identisch sind oder keine bezüglich<br />
des Selektortyps widersprüchliche, namensgleiche Konstruktor-/Selektor-Paare<br />
enthalten. Hier sehen wir die Ähnlichkeit zur Unifikation. Statt einer zeichenweisen<br />
Überprüfung beruht die Typvereinbarkeit auf der Überprüfung der Konstruktor-Selektor-Struktur.<br />
Die in AutoFocus 2 vordefinierten Grundtypen (Boolean, Int, etc.) sind nur<br />
mit sich selbst vereinbar, da sie gar keine Konstruktor-Selektor-Struktur besitzen.<br />
Sie stellen jedoch die Fundierung für die rekursive Definition des Typvereinbarkeitprädikats<br />
dar.<br />
Erweitern wir die anfangs dargestellte Typdefinition um einen zusätzlichen<br />
Datentyp für Systemzustände,<br />
data MySignal = Absent | Present( value:Int );<br />
data AnotherSignal = Present( value:Int ) | Error;<br />
so erhalten wir durch die Unifikation mit Namensgleichheit den folgenden Ergebnistyp:<br />
data MySignal_AnotherSignal =<br />
Absent | Present( value:Int ) | Error;<br />
3.3 Typvereinbarkeitsprädikat in ODL<br />
Nachdem wir nun das Metamodell untersucht und die Typvereinbarkeitsprüfung<br />
theoretisch erläutert haben, können wir nun zur Praxis wechseln und das ODL-<br />
Prädikat programmieren.<br />
Da ODL für rekursive Berechnungen bisher nur die Konstruktion von Fixpunktmengen<br />
zulässt, beschränken wir uns hier auf den analytischen Fall, d.h.<br />
wir wollen die Frage beantworten können, ob zwei gegebene Typdefinitionen<br />
mitein<strong>and</strong>er vereinbar sind. Der konstruktive Fall wäre dann die Berechnung<br />
und Erzeugung des Vereinigungstyps. Diese Berechnung beinhaltet das ermitteln<br />
eines Unifikators, der angibt welche Konstruktoren und Selektoren jeweils<br />
24
unifziert werden. Der Unifikator kann dann benutzt werden um die unifzierte<br />
Typdefinition automatisch zu erzeugen. In Abschnitt 3.4 führen wie die Definition<br />
eines solchen Unifikators interaktiv durch, d.h. der Nutzer wird festgelegen,<br />
welche Konstruktoren und Selektoren unifziert werden sollen, wobei die<br />
Namensgleichheit nicht mehr erforderlich ist.<br />
Um die Frage der Vereinbarkeit zweier Typdefinitionen zu beantworten, konstruieren<br />
wir mit Hilfe eines Fixpunktes die Menge aller vereinbarbaren Typtupel<br />
über zwei Typmodulen (DTDModule im Metamodell). Durch Prüfen, ob ein<br />
gegebenes Paar von Typen in dieser Menge enthalten ist, kann die Frage der<br />
Vereinbarkeit dieser beiden entschieden werden.<br />
In diesem Beispiel lassen wir zu Demonstrationszwecken den Benutzer bestimmen,<br />
von welchen beiden DTD-Modulen (den Sammelbehältern für Datentypdefinitionen)<br />
eine Vereinbarkeitsmenge berechnet werden soll und zeigen ihm<br />
diese am Ende an.<br />
Wir beginnen also mit der Auswahl der beiden DTDModule-Instanzen.<br />
context module1:DTDModule . context module2:DTDModule .<br />
Wir nutzen nun den Fixpunktoperator für die rekursive Mengenkonstruktion.<br />
Die Elemente der Menge sind dabei Tupel aus zwei TConst-Instanzen, je eine<br />
aus einem der beiden DTD-Module. TConst ist die MM-Superklasse, die atomare<br />
Typdefinitionen zusammenfasst (vgl. hierzu Abbildung 3.2).<br />
exists eqTypeSet: lfp fp1 set<br />
fp1_it:(mod1Type:(module1.TConsts), mod2Type:(module2.TConsts))<br />
with (<br />
In die Fixpunktmenge nehmen wir als erstes die identischen Tupel auf.<br />
Dies sind hier nur die AutoFocus 2-Grundtypen (also (Int, Int) und (Boolean,<br />
Boolean), usw.). Die Grundtypen sind in jedem DTD-Modul automatisch eingefügt.<br />
fp1_it.mod1Type = fp1_it.mod2Type or (<br />
Der zweite Teil der Konstruktionsvorschrift für den Fixpunkt übernimmt<br />
nun die Überprüfung, ob zwei nutzerdefinierte Datentypen vereinbar sind. Hierzu<br />
suchen wir die zu dem betrachteten Tupel gehörenden Datendefinitionen<br />
(DataDef-Instanzen). Für Grundtypen existieren, wie bereits gesagt keine Datentypdefinitionen,<br />
weshalb sie auch nur mit sich selbst vereinbar sind.<br />
exists mod1dd:DataDef . (<br />
mod1dd = fp1_it.mod1Type.TypeDef <strong>and</strong><br />
exists mod2dd:DataDef . (<br />
mod2dd = fp1_it.mod2Type.TypeDef <strong>and</strong><br />
Konnten wir entsprechende DataDef-Instanzen für beide Tupelteile finden,<br />
überprüfen wir nun sämtliche Konstruktorkombinationen, ob es namensgleiche<br />
Konstruktoren gibt.<br />
forall con1:(mod1dd.Constructors) .<br />
forall con2:(mod2dd.Constructors) . (<br />
(con1.Name = con2.Name) implies (<br />
25
Haben wir namensgleiche Konstruktoren gefunden, müssen wir überprüfen,<br />
ob diese namensgleiche Selektoren besitzen.<br />
forall sel1:(con1.Selectors) .<br />
forall sel2:(con2.Selectors) . (<br />
(sel1.Name = sel2.Name) implies (<br />
Sollten auch namensgleiche Selektoren existieren, ist für diese nun zu prüfen,<br />
ob deren Typen vereinbar sind, d.h. wir müssen nachsehen, ob unsere Fixpunktmenge<br />
ein entsprechendes Tupel enthält.<br />
exists fp1_elem:fp1 . (<br />
call testSelectorType(sel1, fp1_elem.mod1Type) <strong>and</strong><br />
call testSelectorType(sel2, fp1_elem.mod2Type)<br />
)<br />
or (<br />
call testSelectorType(sel1, fp1_it.mod1Type) <strong>and</strong><br />
call testSelectorType(sel2, fp2_it.mod2Type)<br />
)<br />
... /* lots of ) */<br />
) .<br />
Das Disjunktionsglied benötigen wir für den Fall, der sich ergibt, wenn wir<br />
rekursive Datentypen untersuchen, wenn also die Selektoren den in der Fixpunktiteration<br />
gerade betrachteten Typ besitzen. In diesem Fall muss die Überprüfung<br />
der Vereinbarkeit der Selektortypen dahingehend erweitert werden, dass<br />
auch das aktuell betrachtete Typtupel überprüft wird.<br />
Nachdem nun die Fixpunktmenge der vereinbaren Typen konstruiert ist (Variable<br />
eqTypeSet), lassen wir an dieser Stelle den Benutzer ein Element der Menge<br />
auswählen, um das Ergebnis in einem entsprechenden Dialog darzustellen.<br />
context elem:eqTypeSet . true<br />
3.4 Interaktive Typvereinigung<br />
Bisher haben wir die Unifikation von Typdefinitionen auf die Namensgleichheit<br />
bei Konstruktoren und Selektoren gestützt. Für die Praxis ist dieses Vorgehen<br />
meist unzureichend, da wir mehr an der Struktur, die durch die Typdefinition<br />
festgelegt wird, interessiert sind als an den Bezeichnern in der Definition. Oft<br />
kommt es sogar vor, dass Typdefinitionen strukturell identisch sind, während<br />
der Entwicklungsarbeit allerdings mit unterschiedlichen Bezeichnern versehen<br />
wurden.<br />
Beispielsweise kann man<br />
data Comm<strong>and</strong> = Start | Stop | Reset;<br />
und<br />
data State = On | Off | Init;<br />
26
als unterschiedlich bezeichnete Sichten auf den strukturell gleichen Datentyp ansehen.<br />
Der Fokus liegt im ersten Fall auf den Bezeichnungen der Komm<strong>and</strong>os,<br />
im zweiten Fall auf den Systemzuständen. Treten beide Typen in einer Spezifikation<br />
auf, so könnte sich der Entwickler entscheiden diese zu vereinheitlichen<br />
und durch eine entsprechende Modelltransformation die gesamte Spezifikation<br />
auf den kombinierten Typ<br />
data Comm<strong>and</strong>State = StartOn | StopOff | ResetInit;<br />
umzustellen.<br />
Um den Entwickler somit bereits auf Spezifiationsebene optimal zu unterstützen,<br />
wollen wir in diesem Abschnitt die Typvereinigung interaktiv durchführen<br />
und wie gerade gesehen insbesondere die Einschränkung der Namensgleichheit<br />
aufheben. Für den eben dargestellten Spezialfall (ohne Selektoren)<br />
kann eine einfache ODL-Transformation verwendet werden.<br />
Zunächst wählt der Entwickler aus, welche Datentypdefinitionen vereinigt<br />
werden sollen. Danach gibt er an, wie die Konstruktoren zu kombinieren sind.<br />
/* select datadefs */<br />
context datadefMap:(one:DataDef, two:DataDef) .<br />
/* define unique constructor unification set */<br />
context constructorMap:{testConMap: set (<br />
one:(datadefMap.one.Constructors),<br />
two:(datadefMap.two.Constructors)) |<br />
forall c1:testConMap . forall c2:testConMap . (<br />
c1.one = c2.one equiv c1.two = c2.two<br />
)<br />
} .<br />
Mit diesen Informationen kann nun eine neue Datendefinition erzeugt werden.<br />
Dies geschieht mit dem folgenden ODL-Ausdruck, wobei wir nur die Assoziationen<br />
wie in Abbildung 3.2 erstellen. Wir erzeugen zuerst eine neues DataDef-<br />
Objekt, benennen und verknüpfen es. Danach kreieren wir das zugehörige AbstractType-Objekt,<br />
welches ebenfalls benannt und entsprechend verknüpft wird.<br />
Wir verzichten an dieser Stelle darauf das Ersetzen der ursprünglichen Objekte<br />
in der gesamten Spezifikation durch das neue Objekt darzustellen.<br />
/* create new datadefinition */<br />
exists nd:new DataDef . (<br />
result has DTDModule(nd, datadefMap.one.DTDModule) <strong>and</strong><br />
result has Name(nd,<br />
concat(datadefMap.one.Name, datadefMap.two.Name)) <strong>and</strong><br />
/* create new abstract type */<br />
exists nat:new AbstractType . (<br />
result has Name(nat,<br />
concat(datadefMap.one.Name, datadefMap.two.Name)) <strong>and</strong><br />
result has TConst(nd, nat) <strong>and</strong><br />
result has TConsts(datadefMap.one.DTDModule, nat) <strong>and</strong><br />
/* replace old abstract types with the unified one*/<br />
...<br />
) <strong>and</strong><br />
27
Abschließend erzeugen wir die Konstruktoren, wobei wir hier darauf verzichten<br />
die nicht-unifizierten Konstruktoren in den neuen Datentyp zu kopieren und<br />
die Änderungen im Modell vorzunehmen.<br />
)<br />
exists newConMap: map orig:constructorMap<br />
to copy:new Constructor . (<br />
forall ncm1:newConMap . forall ncm2:newConMap . (<br />
ncm1.orig = ncm2.orig equiv ncm1.copy = ncm2.copy<br />
) <strong>and</strong><br />
forall nc:newConMap . (<br />
/* create new constructors */<br />
result has Name(nc.copy,<br />
concat(nc.orig.one.Name, nc.orig.two.Name)) <strong>and</strong><br />
result has DataDef(nc.copy, nd) <strong>and</strong><br />
/* replace old constructors with the unified ones */<br />
...<br />
)<br />
) <strong>and</strong><br />
/* copy constructors not unified <strong>and</strong> replace old references */<br />
...<br />
Im Folgenden wollen wir nun noch die Selektoren mitberücksichtigen. In<br />
diesem Fall muss es möglich sein mehrere Datendefinitionen gleichzeitig zu unifizieren,<br />
da Selektoren nur unifziert werden können, wenn sie den selben Ergebnistyp<br />
haben oder die Ergebnistypen ebenfalls vereinigt werden. Letzteres<br />
bedeutet, dass die entsprechenden Datendefinitionen vereinigt werden müssen.<br />
context datadefTupel:{<br />
tupel:(datadefMap : set (one:DataDef, two:DataDef),<br />
constructorMap : set (one:Constructor, two:Constructor),<br />
selectorMap : set (one:Selector, two:Selector)<br />
) |<br />
forall sme:(tupel.selectorMap) . (<br />
/* constructor dependency */<br />
exists sme_con:(tupel.constructorMap) . (<br />
sme.one.Constructor = sme_con.one <strong>and</strong><br />
sme.two.Constructor = sme_con.two<br />
) <strong>and</strong> (<br />
/* selector types are equal */<br />
call selectorTypeEqual(sme.one, sme.two) or<br />
/* their datadefs are unified */<br />
exists sme_dd:(tupel.datadefMap) . (<br />
call testSelectorType(sme.one, sme_dd.one.TConst) <strong>and</strong><br />
call testSelectorType(sme.two, sme_dd.two.TConst)<br />
)<br />
)<br />
) <strong>and</strong><br />
forall cme:(tupel.constructorMap) . (<br />
/* datadef dependency */<br />
exists cme_dd:(tupel.datadefMap) . (<br />
28
cme.one.DataDef = cme_dd.one <strong>and</strong><br />
cme.two.DataDef = cme_dd.two<br />
)<br />
)<br />
} . true<br />
Diese interaktive Typunifikation über sämtliche Typdefinitionen eines Modells<br />
wird, wie in Abbildung 3.4, bereits bei einer H<strong>and</strong>voll Datendefinitionen<br />
praktisch nicht mehr nutzbar. Die ausgewählten zu unifizierenden Typdefinitionen<br />
zeigen die rekursive Abhängigkeit zwischen Datendefinitionen, Konstruktoren<br />
und Selektoren. Sie basieren auf folgenden Typdefinitionen:<br />
data Type1 = NoArg1 | Arg1( a1:Int );<br />
data Type2 = NoArg2 | Arg2( a2:Int );<br />
data RecursiveTestType1 = MyTest1( mt1:Type1 );<br />
data RecursiveTestType2 = MyTest2( mt2:Type2 );<br />
Nur der Vollständigkeit wegen, geben wir hier noch das benamte Prädikat<br />
selectorTypeEqual an. Es überprüft zwei Selektoren, ob deren Ergebnistyp gleich<br />
ist. Die verwendeten ODL Mechanismen sind aus Abschnitt 3.1.2 bekannt.<br />
define selectorTypeEqual (<br />
sel1:Selector,<br />
sel2:Selector<br />
) := (<br />
sel1 = sel2 or<br />
/* search function applications */<br />
exists ta:{ta_test:(one:TAppl, two:TAppl) |<br />
ta_test.one = sel1.Type <strong>and</strong><br />
ta_test.two = sel2.Type<br />
} .<br />
exists ta_arg1:(ta.one.TArgs) . (<br />
indexOf(ta_arg1, ta.one, "TArgs") = 1 <strong>and</strong><br />
exists ta_arg2:(ta.two.TArgs) . (<br />
indexOf(ta_arg2, ta.two, "TArgs") = 1 <strong>and</strong><br />
/* compare result types */<br />
ta_arg1 = ta_arg2<br />
)<br />
)<br />
).<br />
3.5 Zusammenfassung<br />
In diesem Kapitel haben wir uns intensiv mit Datentypdefinitionen in AutoFocus<br />
2 und der Typvereinbarkeit bzw. -vereinigung befasst. Wir haben gesehen,<br />
wie Datentypen spezifiziert werden und wie sie gemäß dem AutoFocus 2-<br />
Metamodell in Objektstrukturen umgesetzt werden. Nachdem wir die Unifikation<br />
der Prädikatenlogik auf Datentypen übertragen haben, begannen wir erste<br />
komplexere ODL-Ausdrücke zu formulieren. Wir haben gesehen, wie mit ODL<br />
29
30<br />
Abbildung 3.3: Eingabedialog zur Typunifikation
zwei Datentypen auf Vereinbarkeit geprüft werden. Anh<strong>and</strong> eines einfachen Beispiels<br />
haben wir die interaktive Typvereinigung auch konstruktiv durchgeführt<br />
und schließlich noch die allgemeine Version der interaktiven Vereinigung von<br />
Datentypen, Konstruktoren und Selektoren programmiert. Dieses Wissen wird<br />
uns im Weiteren helfen ein Lösungskonzept für die gestellte Gesamtaufgabe aus<br />
theoretischer Sicht zu entwickeln. Bei der konkreten Umsetzung eines auf diesem<br />
Konzept beruhenden interaktiven Ablaufs werden wir Teile der hier definierten<br />
ODL-Ausdrücke wiederverwenden.<br />
31
Kapitel 4<br />
Spezifikationsvereinigung<br />
aus theoretischer Sicht<br />
Nachdem wir in Kapitel 3 einen ersten Eindruck gewonnen haben, wie mit ODL<br />
das Teilproblem der Typunifikation gelöst wird, wenden wir uns nun wieder<br />
dem Gesamtproblem der Spezifikationsvereinigung zu. In diesem Kapitel werden<br />
wir hierfür ein Lösungskonzept entwickeln und formal fundieren. Wir erläutern<br />
zunächst geeignete Lösungsstrukturen. Über diesen werden wir, ausgehend vom<br />
AutoFocus 2-Metamodellstruktur, Prädikate definieren, welche uns die Lösung<br />
des Vereinigungsproblems als Variablenbelegungsproblem ermöglichen. Eine Betrachtung<br />
des notwendigen Berechnungsaufw<strong>and</strong>es des beschriebenen Verfahrens<br />
schließt dieses Kapitel ab.<br />
Die hier gezeigte theoretische Fundierung ist für das Anwendungsbeispiel aus<br />
Kapitel 2 um viele Modellelemente vereinfacht worden. In Anhang A finden sich<br />
die für das Anwendungsbeispiel relevanten, zusätzlichen Theorieüberlegungen,<br />
die aber im Wesentlichen den hier beschriebenen entsprechen.<br />
4.1 Definition des Lösungsraumes<br />
Im Einführungsbeispiel (siehe Kapitel 3) zu ODL haben wir gesehen, wie die Typunifikation<br />
realisiert wurde. Das Problem der Spezifikationsvereinigung werden<br />
wir ebenfalls mit Hilfe eines der Unifikation ähnlichen Ablaufs lösen.<br />
In Abschnitt 3.4 haben wir gesehen, wie die Typvereinigung mit ODL interaktiv<br />
durchgeführt wird. Die Eingabe des Benutzers waren dabei Mengen<br />
von 2-Tupeln über den Modellelementmengen der Datendefinitionen DataDef,<br />
Konstruktoren Constructor und Selektoren Selector. Verallgemeinern wir diese<br />
Strukturen, so kommen wir zu Relationen von zu unifizierenden Modellelementen,<br />
die wir im folgenden als Unifikationsrelationen bezeichnen.<br />
Wir erhalten jeweils eine Unifikationsrelation für jedes Metamodellelement,<br />
also für jede Klasse von Modellelementen. Für unser Beispiel sind dies die folgenden<br />
Relationen:<br />
componentMap ∈ P( Component × Component )<br />
portMap ∈ P( P ort × P ort )<br />
32
stateMap ∈ P( State × State )<br />
transitionMap ∈ P( T ransitionSegment × T ransitionSegment )<br />
inputMap ∈ P( Input × Input )<br />
outputMap ∈ P( Output × Output )<br />
datadefMap ∈ P( DataDef × DataDef )<br />
constructorMap ∈ P( Constructor × Constructor )<br />
selectorMap ∈ P( Selector × Selector )<br />
Wählen wir für jede Unifikationsrelation eine konkrete Instanz, so bildet<br />
das Tupel dieser Instanzen einen Unifikator, wenn es die in Abschnitt 4.3 beschriebenen<br />
Abhängigkeiten erfüllt. Der Unifikator bildet die Grundlage für den<br />
zweiten Schritt der Spezifikationsvereinigung, die Konstruktion, die wir in Kapitel<br />
5 weiter ausführen. Die Relationen geben dabei an, aus welchen bestehenden<br />
Modellelementen die neu erzeugten Modellelemente entstehen.<br />
Ein einfacher Unifikator ist das Tupel der Identitätsrelationen. Jedes Modellelement<br />
würde hierbei mit sich selbst vereinigt, was bei der konstrutiven<br />
Durchführung eine Kopie des Produktmodells erzeugt. Wählten wir für jede Unifikationsrelation<br />
hingegen jeweils die leere Relation, so würde das AutoFocus 2-<br />
Produktmodell überhaupt nicht verändert. In beiden Fällen h<strong>and</strong>elt es sich also<br />
um in der Theorie korrekte Lösungen bzw. Unifikatoren, welche mit ODL auch<br />
technisch umsetzbar sind, aber keine praktische Relevanz haben.<br />
4.2 Unifikationsrelationen<br />
Die im letzten Abschnitt eingeführten Unifikationsrelationen können im Hinblick<br />
auf die Konstruktion einer Vereinigungsspezifikation unterschiedlich interpretiert<br />
werden. Im Einzelfall bieten sich alternative Definitionen für die Unifikation<br />
der betrachteten Modellelemente an. Wir werden beides hier anh<strong>and</strong><br />
von Beispielen sehen und am Ende dieses Abschnitts eine Festlegung für die<br />
konstruktive Durchführung der Spezifikationsvereinigung in Kapitel 5 treffen.<br />
4.2.1 Alternative Relationsdefinition<br />
Am Beispiel der Automatenbeschreibungen wollen wir aufzeigen, dass es auch<br />
möglich ist <strong>and</strong>ere Definitionen für die Unifikationsrelationen zu verwenden.<br />
Abbildung 4.1 zeigt eine <strong>and</strong>ere Vereinigung der Spezifikationen des Verhaltens<br />
unserer Beispielkomponenten. Wir vereinigen jetzt zwei Zustände der Komponente<br />
ErrorTreatment mit einem Zust<strong>and</strong> der Komponente Press, nämlich<br />
Init und Stop mit Off.<br />
Hierfür definieren wir die Unifikationsrelation, wie folgt, über den Potenzmengen<br />
der Modellelementmengen:<br />
stateMap ∈ P ( P(State) × P(State) )<br />
Die dem Beispiel entsprechende konkrete Unifikationsrelation hat dann folgende<br />
Form:<br />
{ ({Initial, ) }<br />
stateMap = Stop}, {Off}<br />
33
ErrorTreatment Verhalten<br />
:Cntrl?Start:<br />
Sts!Started:<br />
Operate<br />
Press Verhalten<br />
Operate<br />
Init<br />
:Cntrl?Stop:<br />
Sts!Stopped:<br />
::Sts!Error:<br />
errCode=1<br />
errCode=0:Cntrl?Start:Sts!Started:<br />
D<br />
:Cmd!Off::<br />
:Cntrl?Reset:<br />
Sts!Initial:<br />
errCode=0<br />
Stop<br />
Off<br />
MergedController Verhalten<br />
Operate<br />
Operate<br />
:CntrlCmd?StopOff:<br />
Sts!Stopped:<br />
D<br />
::Sts!Error:<br />
errCode=1<br />
errCode=0:<br />
CntrlCmd!StartOn:<br />
Sts!Started:<br />
StopInitOff<br />
:CntrlCmd?Reset:<br />
Sts!Initial:<br />
errCode=0<br />
:Cmd!On::<br />
Abbildung 4.1: Alternative Vereinigung der Automatenzustände<br />
Da bei dieser Definition die in Abschnitt 4.3 eingeführten Abhängigkeiten<br />
und die in Abschnitt 5.5 gezeigte Konstruktion ebenfalls auf die Potenzmengenbetrachtung<br />
angepasst werden müssen und dadurch zusätzliche Iterationen und<br />
Quantifizierungen notwendig sind, die das Verständnis erschweren, bleiben wir<br />
an dieser Stelle bei der einfachen Variante.<br />
4.2.2 Nutzung zusätzlicher Transformationsschritte<br />
Wir können auch die Modellierungsmöglichkeiten von AutoFocus 2 nutzen und<br />
die Zustände Initial und Stop aus Abbildung 4.1 zu einem hierarchischen Zust<strong>and</strong><br />
zusammenfassen. Dieser einzelne neue Zust<strong>and</strong> kann dann wiederum mit<br />
einem einelnen Zust<strong>and</strong> unifiziert werden. Dies entspricht also wieder der Definition<br />
der Unifikationsrelationen in Abschnitt 4.1.<br />
Wir ändern bei dieser Variante also nicht die Definition der Relationen,<br />
sondern nutzen zusätzliche Transformationsschritte um das Modell vorher in<br />
eine entsprechende Form zu transformieren.<br />
Diese Variante werden wir in dieser Arbeit allerdings nicht weiter verfolgen.<br />
Es sei aber angemerkt, dass bei der Zusammenfassung von Zuständen zu einem<br />
hierarchischen Zust<strong>and</strong> auch beachtet werden muss, wie die ein- und ausgehenden<br />
Transitionen transformiert werden. Sie müssen in entsprechende Segmente<br />
zerlegt werden oder umgekehrt zusammengefasst werden.<br />
4.2.3 Interpretation der Unifikationsrelation<br />
Für das Konstruktionsverfahren haben wir verschiedene Möglichkeiten die Unifikationsrelationen<br />
zu interpretieren. Betrachten wir die folgende Relation über<br />
Komponenten:<br />
componentMap = { (CompA, CompB), (CompB, CompC) }<br />
Es ergeben sich nun die folgenden zwei Interpretationen:<br />
34
1. Es werden zwei neue Komponenten erzeugt, wobei die erste durch Vereinigung<br />
von CompA und CompB, die zweite durch Vereinigung von CompB<br />
und CompC entsteht.<br />
2. Vom Konstruktionsverfahren könnte genauso erwartet werden, dass die<br />
angegebene Relation unvollständig ist und zunächst mittels der Bildung<br />
der transitiven Hülle vervollständigt werden muss. Somit könnte obige<br />
Relation also auch dafür stehen, dass nur eine neue Komponente erzeugt<br />
wird, die aus der Vereinigung von CompA, CompB und CompC entsteht.<br />
Dieser Fall entspricht der vorher erläuterten alternativen Möglichkeit mit<br />
Potenzmengen zu arbeiten.<br />
In Kapitel 5 verwenden wir die zusätzliche Bedingung, dass Unifikationsrelationen<br />
partielle, ein-eindeutige Funktionen sein müssen. Auch wenn dadurch die<br />
in Abbildung 4.1 gezeigte Spezifikationsvereinigung nicht mehr möglich ist, denn<br />
jeder Zust<strong>and</strong> der ersten Komponente kann nur mit höchstens einem Zust<strong>and</strong><br />
der zweiten Komponente unifiziert werden und vice versa. Diese Vereinfachung<br />
ist aus Gründen der Verständlichkeit der ODL-Ausdrücke zur Definition der<br />
Unifikationsrelationen und der Generierungsausdrücke notwendig.<br />
4.3 Abhängigkeiten der Modellelemente<br />
Die Assoziationen und Beziehungen der Modellelemente, wie sie durch das Metamodell<br />
festgelegt werden, haben auch eine Bedeutung in Bezug auf das Vereinigungsproblem.<br />
Sie stellen genau die Abhängigkeiten der Modellelemente dar, die<br />
bei der Unifikation beachtet werden müssen. Abbildung 4.2 zeigt den Abhängigkeitsgraphen,<br />
der sich aus dem Metamodell ergibt. Die gerichteten Kanten lassen<br />
sich hierbei nach ihrem Verlauf in interne Abhängigkeiten (Kanten mit gleichem<br />
Start- und Zielknoten) und externe Abhängigkeiten (Kanten mit verschiedenen<br />
Start- und Zielknoten) einteilen.<br />
4.3.1 Interne Abhängigkeiten<br />
Wir sprechen von einer (unifikationsrelations-)internen Abhängigkeit, wenn die<br />
zwei zu unifizierenden Modellelemente bezüglich eines Attributwerts gleich sein<br />
müssen, damit die Unifikation aus semantischer Sicht sinnvoll ist. Solche Abhängigkeiten<br />
beziehen sich auf eine einzelne Modellelementklasse. In Abbildung<br />
4.2 sind sie als gestrichelte Kanten eingezeichnet.<br />
Eine interne Abhängigkeit ist ein Prädikat, dass von allen Tupeln einer Unifikationsrelation<br />
erfüllt werden muss und insbesondere nicht von <strong>and</strong>eren Unifikationsrelationen<br />
abhängt. Im Abhängigkeitsgraph sind dies reflexive Kanten.<br />
Solche Prädikate haben folgende Grundstruktur (one ist das erste, two das zweite<br />
Element der Tupel in u):<br />
∀u ∈ UnifRel : u.one.SomeAttribute = u.two.SomeAttribute<br />
Im Beispiel ist dies bei der Unifikationsrelation von Port der Fall. Es können<br />
nur solche Ports mitein<strong>and</strong>er unifiziert werden, die die gleiche Richtung aufweisen,<br />
d.h. in Bezug auf das Direction-Attribut äquivalent sind. Die Unifikation<br />
eines Eingabeports mit einem Ausgabeport ist syntaktisch möglich, denn beide<br />
35
DataDefinition<br />
Component<br />
Constructor<br />
Port<br />
State<br />
Selector<br />
Transition<br />
Input / Output<br />
Abbildung 4.2: Abhängigkeitsgraph der Metamodellelemente<br />
gehören zur Metamodellklasse Port. Dies hat aber keine sinnvolle Bedeutung,<br />
da AutoFocus 2-Modelle keine bidirektionalen Schnittstellen an Komponenten<br />
haben.<br />
Das Beispiel setzen wir direkt in ODL um. Wir erhalten folgenden Teilausdruck:<br />
forall pme:portMap .<br />
pme.one.Direction.IsEntry = pme.two.Direction.IsEntry<br />
Mit portMap ist hier die Unifikationsrelation bezüglich Port bezeichnet; im<br />
ODL Typsystem hat portMap den Typ set (Port, Port). Direction ist lediglich<br />
das Kapselobjekt, das den boolschen Wert IsEntry umschließt. Letzterer zeigt<br />
die Richtung, also Ein- oder Ausgabe, an.<br />
4.3.2 Externe Abhängigkeiten<br />
Im Gegensatz zu internen Abhängigkeiten stellen die externen Abhängigkeiten<br />
Bedingungen zwischen verschiedenen Modellelementklassen dar (nicht-reflexive<br />
Kanten des Abhängigkeitsgraphen). Diese Prädikate sind Bedingungen zwischen<br />
zwei Unifikationsrelationen mit folgender Struktur:<br />
∀u 1 ∈ UnifRel 1 : ∃u 2 ∈ UnifRel 2 :<br />
u 1 .one.AssociatedEntity = u 2 .one ∧ u 1 .two.AssociatedEntity = u 2 .two<br />
Die gerichteten Kanten des Graphen in Abbildung 4.2 sind so zulesen, dass<br />
der Startknoten der allquantifizierten Tupelmenge UnifRel 1 und der Zielknoten<br />
der existenzquantifizierten Tupelmenge UnifRel 2 entspricht.<br />
36
Betrachten wir nun die im Beispiel vorh<strong>and</strong>ene externe Abhängigkeit zwischen<br />
Port und Component. Diese sagt aus, dass es für alle Tupel in der Unifikationsrelation<br />
von Port ein Tupel in der Unifikationsrelation von Component geben<br />
muss, so dass die unifizierten Ports zu unifizierten Komponenten korrespondieren.<br />
Auch diese Abhängigkeit kann wieder direkt in ODL formuliert werden:<br />
forall pme:portMap .<br />
exists cme:componentMap . (<br />
pme.one.Component = cme.one <strong>and</strong> pme.two.Component = cme.two)<br />
Mit componentMap ist hier die Unifikationsrelation bezüglich Component bezeichnet.<br />
Der Selektorausdruck .Component entspricht der Navigation im Metamodell<br />
von Port entlang der Aggregation zu Component.<br />
4.4 Lösungsverfahren<br />
Nachdem wir nun die Interpretation der Kanten des Abhängigkeitsgraphen<br />
erläutert haben, wenden wir uns der Interpretation der Knoten zu.<br />
Wie in Abschnitt 4.1 gezeigt, suchen wir für jedes relevante Metamodellelement<br />
eine Unifikationsrelation. Jeder Knoten im Abhängigkeitsgraph liefert<br />
uns hierbei ein Prädikat, dass diese Relation, im Hinblick auf eine korrekte Gesamtlösung,<br />
erfüllen muss. Das Prädikat ergibt sich aus den jeweiligen Abhängigkeiten<br />
(ausgehende Kanten) durch deren Verknüpfung mit logischem Und.<br />
Die Umsetzung in ODL optimieren wir, indem wir die identischen Allquantoren<br />
zusammenfassen und nur die Unterausdrücke mit <strong>and</strong> verknüpfen. Hier<br />
wieder das Beispiel von Port:<br />
forall pme:portMap . (<br />
/* component dependency */<br />
exists cme:componentMap . (<br />
pme.one.Component = cme.one <strong>and</strong> pme.two.Component = cme.two<br />
) <strong>and</strong><br />
/* data definition dependency */<br />
exists dme:datadefMap . (<br />
/* equivalence is quite complex here */<br />
/* due to class hierarchies */<br />
pme.one.DefinedType.Model = dme.one.TConst <strong>and</strong><br />
pme.two.DefinedType.Model = dme.two.TConst<br />
) <strong>and</strong><br />
/* internal dependency */<br />
pme.one.Direction.IsEntry = pme.two.Direction.IsEntry<br />
)<br />
Mit Hilfe dieser Prädikate können wir nun alle Lösungen des Vereinigungsproblems<br />
berechnen. Die zu evaluierende Formel besteht aus der Existenzquantifizierung<br />
aller Unifikationsrelationen und der Und-Vernüpfung der Knotenprädikate.<br />
exists componentMap:set (Component, Component) .<br />
exists portMap:set (Port, Port) .<br />
37
...<br />
exists constructorMap: set (Constructor, Constructor) .<br />
exists datadef:set (DataDef, DataDef) . (<br />
forall pme:portMap . ( ... ) <strong>and</strong><br />
forall come:constructorMap . ( ... ) <strong>and</strong><br />
...<br />
)<br />
Die Relationen (im Beispiel portMap, datadefMap, componentMap, usw.) sind also<br />
die Variablen eines Belegungsproblems. Leicht zu folgern ist, dass für dieses<br />
Problem eine triviale Lösung (die leere Relation für alle Unifikationsrelationen)<br />
existiert.<br />
4.5 Komplexitätsbetrachtung<br />
Am Ende dieses Kapitels wenden wir uns der Frage zu, mit welchem Berechnungsaufw<strong>and</strong><br />
das hier gezeigte Lösungsverfahren funktioniert. Wir werden sehen,<br />
dass der Aufw<strong>and</strong> bereits für sehr kleine AutoFocus 2-Modelle exponentiell<br />
groß wird.<br />
Um alle möglichen Lösungen des Vereinigungsproblems zu finden müssen wir<br />
sämtliche Unifikatork<strong>and</strong>idaten auf ihre Sinnfälligkeit prüfen, d.h. die im vorherigen<br />
Abschnitt gezeigten Prädikate bezüglich dieser K<strong>and</strong>idaten evaluieren.<br />
Die Anzahl solcher K<strong>and</strong>idaten lässt sich mit dem Wissen über den Aufbau des<br />
Lösungsraumes (siehe Abschnitt 4.1) leicht ermitteln. Für jede Unifikationsrelation<br />
gibt es<br />
2 (u·u)<br />
Möglichkeiten. u ist dabei die Anzahl der Modellelemente dieser Relation, also<br />
bspw. die Anzahl der Modell vorh<strong>and</strong>enen Komponenten.<br />
Aufgrund der Abhängigkeiten müssen wir zusätzlich alle Kombinationen von<br />
konkreten Unifikationsrelationen ausprobieren. Dies entspricht genau der Existenzquantifizierung<br />
der Unifikationsrelationen, wie im vorherigen Abschnitt dargestellt.<br />
Insgesamt erhalten somit<br />
2 u2 1 +u2 2 +...+u2 k<br />
Unifikatork<strong>and</strong>idaten. In unserem Beispiel ist k = 9 wegen der neun Modellelementklassen.<br />
Hieraus ergibt sich, dass selbst für die kleinsten praxisnahen Modelle<br />
die Berechnung sämtlicher Unifikatoren nicht effizient durchgeführt werden<br />
kann.<br />
4.6 Zusammenfassung<br />
In diesem Kapitel haben wir das Problem der Spezifikationsvereinigung aus<br />
theoretischer Sicht untersucht. Wir haben zunächst passende Lösungsstrukturen<br />
definiert, die Unifikationsrelationen. Daraufhin erfolgte eine genauere Betrachtung<br />
dieser Relationen, insbesondere ihre Interpretation im Hinblick auf<br />
die konstruktive Vereinigung von Spezifikationen und alternative Definitionen<br />
für diese Relationen.<br />
38
Ausgehend vom AutoFocus 2-Metamodell haben wir dann Abhängigkeiten<br />
zwischen den einzelnen Unifikationsrelationen untersucht und jeweils ein Schema<br />
zur Umsetzung in ODL angegeben. Somit st<strong>and</strong>en uns die notwendigen<br />
Hilfsmittel zur Verfügung um das Lösungsverfahren festzulegen, welches wir<br />
abschließend auf den Berechnungsaufw<strong>and</strong> hin untersuchten. Es muss hierzu<br />
angemerkt werden, dass eine Erweiterung der Unifikation auf komplexere Ein-<br />
/Ausgabeterme und die Hinzunahme der Vorbedingungen und Aktionen von<br />
Transitionen, eine Erweiterung der hier beschriebenen Abhängigkeiten auf polymorphe<br />
Fälle erfordert, d.h. wir müssen auch die <strong>and</strong>ere mögliche Unterklassen<br />
von Termen berücksichtigen und können nicht wie hier von Konstruktoren allein<br />
ausgehen. Wir gehen hierauf auch kurz bei den Anmerkungen zum ODL<br />
Programm im Anhang A ein.<br />
Wie wir gesehen haben, ist der Berechnungsaufw<strong>and</strong> aller Lösungen bereits<br />
für kleine Modelle sehr hoch. Ausserdem sind viele der Lösungen für die praktische<br />
Umsetzung nicht relevant. Im folgenden Kapitel wollen die hier eingeführte<br />
theoretische Grundlage für ein interaktive Unterstützung des Entwicklers nutzen,<br />
um somit ein methodisch sinnvolles und effizientes Transformationsverfahren<br />
zu erhalten und insbesondere die Brücke in die praktische Anwendung<br />
schlagen.<br />
39
Kapitel 5<br />
Interaktive<br />
Spezifikationsvereinigung<br />
Nachdem wir nun formal gezeigt haben, wie das Problem der Spezifikationsvereinigung<br />
gelöst werden kann, wenden wir uns nun einer konkreten Umsetzung<br />
in AutoFocus 2 und ODL zu.<br />
Zunächst werden wir das interaktive Vorgehen motivieren und die Brücke<br />
von der Theorie in die Praxis schlagen. Der interaktive Ablauf besteht aus zwei<br />
Phasen, die wir getrennt vonein<strong>and</strong>er darstellen werden. Als Erstes wird der analytische<br />
Teil des interaktiven Ablaufs in eine Folge von einzelnen Dialogschritten<br />
gegliedert, wobei wir ebenfalls alternative Abläufe beleuchten. Danach werden<br />
wir die wesentlichen Dialoge dieses Teils mit ODL formulieren und verschiedene<br />
Varianten illustrieren.<br />
Im letzten Abschnitt dieses Kapitels erläutern wir die technische Umsetzung<br />
des zweiten, konstruktiven Teils der Spezifikationsvereinigung in ODL.<br />
5.1 Übergang zum interaktiven Ablauf<br />
In Kapitel 4 interessierten wir uns für die Berechnung aller Lösungen der Spezifikationsunifkation<br />
für ein gegebenes Modell. Aus Sicht des Entwicklers ist<br />
es allerdings nicht relevant, alle diese Lösungen zu berechnen. Er möchte vielmehr<br />
die Unterstützung in Form von Einzelschritten, sodass er durch mehrere<br />
Einzelentscheidungen die Gesamtlösung zusammensetzt. Wir werden daher die<br />
Erkenntnisse aus der Theorie nutzen, um einen interaktiven Unifikationsablauf<br />
zu erstellen. Ein möglicher Anfang und ein Beispiel für Einzelschritte wäre hier<br />
zuerst die Auswahl welche beiden Komponenten vereinigt werden sollen, gefolgt<br />
von der Entscheidung wie die Schnittstelle zu unifizieren ist.<br />
Das interaktive Vorgehen ist aus methodischer Sicht besser, da wir gezielt<br />
nur auf die Lösung hinarbeiten, die der Nutzer auch möchte, anstatt alle möglichen<br />
Lösungen zu berechnen. Gleichzeitig erhalten wir durch die Einführung der<br />
Interaktion ein effizientes Verfahren, da jeweils nur die Einzelentscheidungen im<br />
Kontext der vorhergehenden Entscheidungen auf ihre Durchführbarkeit geprüft<br />
werden müssen und nicht eine erschöpfende Suche über alle Unifikationsrelationen<br />
durchgeführt werden muss.<br />
40
Für die interaktive Spezifikationsvereinigung nutzen wir die Dialogfähigkeiten<br />
von ODL und gliedern den Ablauf in mehrere Dialogeinzelschritte. In jedem<br />
wird der Entwickler nach einzelnen Teilen des aus dem Theorieteil bekannten<br />
Unifikators gefragt, wobei ständig die Zulässigkeit seiner Entscheidungen überprüft<br />
wird. Es ist ihm nur erlaubt von einem Dialog aus fortzufahren, wenn die<br />
von ihm definierte Unifikationsrelation bezüglich der Abhängigkeiten mit seinen<br />
früheren Entscheidungen verträglich ist. Andernfalls wird eine Fortsetzung des<br />
interaktiven Ablaufs verhindert bis die Korrektheit gemäß den Abhängigkeiten<br />
hergestellt ist oder abgebrochen wird.<br />
Bei der Definition des Ablaufs mittels ODL könnten wir sehr schematisch<br />
vorgehen und für jedes Metamodellelement bzw. jede Unifikationsrelation einen<br />
gleichartigen Abfragedialog definieren. Da wir jedoch maximalen Nutzen aus<br />
den Entscheidungen des Nutzers ziehen wollen, werden wir hier eine nichtschematische<br />
Variante des ODL-Ablaufs aufbauen und für jeden Teil des Unifikators<br />
einen möglichst nutzerfreundlichen Dialog konstruieren, indem wir gewisse<br />
Vorberechnungen vornehmen. Die interne Abhängigkeit bei Port bietet sich<br />
hierfür beispielsweise an.<br />
Desweiteren werden wir einige Unifikationsrelationen automatisch bestimmen,<br />
indem wir die Abhängigkeiten und die getroffenen Festlegungen zur Berechnung<br />
nutzen. Dies gilt im folgenden für den Bereich der Datendefinitionen,<br />
obwohl auch hier eine interaktive Variante denkbar ist (vgl. Abschnitt 3.4).<br />
5.2 Festlegung der Dialogführung<br />
In Abbildung 4.2 auf Seite 36 haben wir die für die Spezifikationsvereinigung<br />
relevanten Metamodellelemente und deren Abhängigkeiten gesehen. Für jedes<br />
Element ergab sich dabei genau eine Unifikationsrelation. Diese werden wir nun<br />
nicht durch existenzielle Quantifizierung, wie in Abschnitt 4.4, berechnen, sondern<br />
interaktiv durch den Entwickler bestimmen lassen. Die Forderung, dass<br />
Unifikationsrelationen partielle ein-eindeutige Funktionen sein müssen halten<br />
wir aufrecht. Wie in Abschnitt 4.2 gezeigt, ist dies eine Hilfsforderung um den<br />
Konstruktionsteil nicht zu verkomplizieren.<br />
Die naheliegende Umsetzung, nämlich jede Relation vom Nutzer aus allen<br />
vorh<strong>and</strong>enen Modellelementen auszuwählen, liefert zwar ein effektives, interaktives,<br />
aber kaum praktikables und auch nicht effizientes Verfahren. Wir werden daher<br />
der Reihe nach alle Unifikationsrelationen durchgehen und eine jeweils günstige<br />
Dialogvariante erläutern. Insbesondere möchten wir die Auswahlmöglichkeiten<br />
eines Dialogs auf das zulässige Minimum beschränken. Wir nutzen dazu<br />
die festgelegten Abhängigkeiten. Bei der Reihenfolge der Einzelschritte orientieren<br />
wir uns an den Modellierungssichten von AutoFocus 2. Beginnend mit der<br />
Struktursicht, kommen wir über die Verhaltenssicht schließlich zur Datensicht.<br />
Die hier beschriebene Dialogführung entspricht dem Programmlisting in Anhang<br />
A. Sie erhebt nicht den Anspruch absolut zu sein, sondern stellt vielmehr<br />
eine Möglichkeit dar, die Spezifikationsvereinigung interaktiv umzusetzen. An<br />
den entsprechenden Stellen geben wir jeweils den Hinweis auf alternative Dialogvarianten,<br />
die meist auch eine Anpassung des in Abschnitt 5.5 beschriebenen<br />
Konstruktionsteils erfordern.<br />
41
5.2.1 Struktursicht<br />
Die Struktursicht umfasst die Metamodellelemente Component und Port.<br />
Component<br />
Der Entwickler trifft als erstes die Entscheidung, welche beiden Komponenten<br />
er vereinigen möchte. Im Gegensatz zur allgemeinen Definition in Kapitel 4,<br />
verwenden wir anstatt der Unifikationsrelation also nur ein Tupel von Komponenten.<br />
Sollen mehrere Komponenten vereinigt werden, ist der Gesamtablauf<br />
mehrmals durchzuführen. Als Dialogansicht verwenden wir die Darstellung zweier<br />
Listen der im Modell vorh<strong>and</strong>enen Komponenten, wie in Abbildung 5.1.<br />
Abbildung 5.1: Dialog für Komponenten (Component)<br />
Port<br />
Aufgrund der Abhängigkeiten können wir die Entscheidungsmöglichkeiten des<br />
Entwicklers bei der Schnittstellenvereinigung unter zu Hilfenahme der vorangegangenen<br />
Entscheidung über die Komponenten schon einschränken. Wir bieten<br />
nur die Port-Elemente zur Auswahl an, die der ersten bzw. zweiten Komponente<br />
entsprechen. Desweiteren führen wir eine Vorberechnung durch und lassen den<br />
Entwickler die von ihm gewünschten aus der Menge der Port-Tupel auswählen,<br />
wobei diese Tupel bezüglich der internen Abhängigkeit von Port über das Richtungsattribut<br />
bereits zulässig sind. Abbildung 5.2 zeigt den Dialog mit der Liste<br />
der berechneten Tupel.<br />
Eine Dialogvariante wäre die Aufteilung der Port-Vereinigung in zwei Dialogschritte,<br />
jeweils einer für die Ein- und für die Ausgabeschnittstellen. Als Darstellung<br />
könnten wir dabei die Ansicht wie für die Zust<strong>and</strong>svereinigung (siehe<br />
42
Abbildung 5.2: Dialog für Schnittstellen (Port)<br />
nächster Abschnitt) wählen. Um den im Programmlisting verwendeten Konstruktionsteil<br />
weiter verwenden zu können, müssten die Ergebnisse der beiden<br />
Dialoge allerdings erst zu einer Menge zusammengefasst werden. Alternativ kann<br />
natürlich auch die Konstruktion angepasst werden.<br />
5.2.2 Verhaltenssicht<br />
Die Verhaltenssicht umfasst die Metamodellelemente State, Transition, Input<br />
und Output.<br />
State<br />
Bei der Vereinigung der Zustände sind wir voll auf die Entscheidungen des Benutzers<br />
angewiesen. Wir bieten ihm daher, wie in Abbildung 5.3 ersichtlich, die<br />
Möglichkeit die Unifikationsrelation vollständig selbst zu definieren. Die einzige<br />
Einschränkung, die wir bereits angew<strong>and</strong>t haben, ist der Inhalt der beiden<br />
Zust<strong>and</strong>slisten, die die Zustände der jeweiligen Komponenten, wie sie im ersten<br />
Schritt gewählt wurden, enthält.<br />
Transition<br />
Wie in Abbildung 5.4 zu sehen, haben wir den Dialog für die Zust<strong>and</strong>sübergänge<br />
mit einer Vorberechnung vereinfacht. Der Nutzer bekommt nur die Transitionspaare<br />
zur Auswahl, die garantiert mit seiner Entscheidung über die Unifikation<br />
von Zuständen im vorherigen Dialogfenster vereinbar sind. Eine Dialogform<br />
analog der vorherigen, erscheint nicht günstiger, da es für Transitionen ja gleich<br />
43
Abbildung 5.3: Dialog für Zustände (State)<br />
44
zwei Abhängigkeiten zu der Zust<strong>and</strong>srelation gibt (Start- und Zielzust<strong>and</strong> der<br />
Transition). Durch die Vorberechnung der möglichen Tupel, erhöhen wir die<br />
Übersichtlichkeit und Verwendbarkeit dieses Dialogschrittes erheblich.<br />
Abbildung 5.4: Dialog für Zust<strong>and</strong>sübergänge (Transition)<br />
Input / Output<br />
Die Verschmelzung von Ein- und Ausgabeausdrücken führen wir nicht mehr<br />
interaktiv durch, sondern berechnen die entsprechende Unifikationsrelation unter<br />
Verwendung der Relationen von Transition und Port. Es werden also die<br />
Input- bzw. Output-Elemente vereinigt, die zu unifizierten Transitionen gehören<br />
und sich auf unifizierte Schnittstellen beziehen. Den dritten für die Vereinigung<br />
relevanten Wert, das Ein-/Ausgabesignal (Constructor), beachten wir an dieser<br />
Stelle noch nicht, sondern verwenden die hier berechneten Unifikationsrelationen<br />
später als Bedingung für die Berechnung der Constructor-Relation.<br />
5.2.3 Datensicht<br />
Die Verhaltenssicht umfasst die Metamodellelemente DataDef, Constructor, und<br />
Selector. In Kapitel 3 haben wir gesehen, wie ein Dialog für die Unifikation<br />
von Datentypdefinition aussehen könnte. Hier wollen wir nun die sich aus den<br />
vorherigen Entscheidungen ableitenden, unifzierten Typdefinitionen berechnen<br />
und somit die Datensicht bei der Spezifikationsvereinigung vollständig automatisieren.<br />
Constructor<br />
Wir beginnen die Berechnungen für die Datensicht mit der Konstruktorunifikation.<br />
Aus den zuvor berechneten Unifikationsrelationen für Input und Output<br />
45
leiten wir die zu vereinigenden Konstruktorpaare ab. Dazu betrachten wir die<br />
Termstruktur der Ein-/Ausgabeausdrücke.<br />
Wir erhalten drei Fälle die sich aus der Termstruktur ergeben:<br />
1. Die Ausdrücke sind parameterlose Konstruktorterme, d.h. wir können direkt<br />
ableiten, dass diese Konstruktoren vereinigt werden müssen. Im Beispiel<br />
tritt nur dieser Fall auf.<br />
2. Die zwei Konstruktoren treten als angew<strong>and</strong>te Funktionen in den Applikationstermen<br />
auf, beispielsweise Const-One(...) und Const-Two(...).<br />
3. Die zwei Konstruktoren treten als Parameterwerte in den Applikationstermen<br />
auf.<br />
Der zweite und dritte Fall kann bei komplexeren Termen mehrmals geschachtelt<br />
auftreten, beispielsweise wenn als Parameter für eine Konstruktorapplikation<br />
wieder eine parametrierte Konstruktorapplikation verwendet wird. Deshalb<br />
werden wir die Konstruktoranalyse mittels einer Fixpunktmenge durchführen.<br />
Wir untersuchen alle unifizierten Ein-/Ausgabeausdrücke ihrer Struktur nach<br />
rekursiv auf Applikationselemente und leiten aus diesen die zu unifzierenden<br />
Konstruktoren ab. Hierfür ist zusätzlich die Berechnung der Unifikationsrelation<br />
für Appl-Elemente, also Funktionsapplikationen, notwendig.<br />
DataDef<br />
Die Unifikationsrelation für DataDef-Elemente leitet sich aus zwei bisherigen<br />
Entscheidungen ab. Erstens ist für jedes Paar von unifizierten Port-Elementen<br />
deren Typen zu unifizieren. Zweitens müssen für zwei unifizierte Konstruktoren<br />
auch deren Datendefinitionen vereinigt werden.<br />
Da wir die Berechnung der Konstruktorunifikation vorgezogen haben, können<br />
wir die Unifikationsrelation für DataDef nun leicht berechnen.<br />
Selector<br />
Selektoren stellen keine Teile der Terme für Ein- und Ausgaben dar, wie die<br />
Konstruktoren. Aus Sicht der Datendefinitionen müssen wir sie jedoch auch einer<br />
Unifikation unterziehen, damit die generierten Terme auch der generierten<br />
Datendefinition entsprechen. In dem hier vorgestellten Ablauf wird die Selektorunifikation<br />
ebenfalls berechnet und nicht dem Benutzer übertragen.<br />
Für die Berechnung stehen uns bei Selektoren verschiedene Möglichkeiten<br />
zur Verfügung die Vereinigung durchzuführen. Zunächst können wir die Namensgleichheit,<br />
wie in Kapitel 3, als Vereinigungskriterium heranziehen. Desweiteren<br />
können wir den Ergebnistyp des Selektors benutzen, um zu unifzierende Selektoren<br />
zu identifizieren. Dabei treten allerdings sehr leicht Mehrdeutigkeiten<br />
auf, die wir eventuell wieder mit Hilfe des Nutzers lösen können. An dieser<br />
Stelle wollen wir die dritte Möglichkeit verwenden, nämlich die Struktur der<br />
Datendefinitionen. Wir unifizieren die Selektoren also nach ihrer Position in der<br />
jeweiligen Konstruktordefiniton.<br />
Als Grundlage für die Berechnung verwenden wir die Unifikationsrelationen<br />
von DataDef und Constructor. Es werden die Selektoren vereinigt, die zu vereinigten<br />
Konstruktoren gehören. Per Definition der Constructor-Relation muss<br />
46
es mindestens einen Term geben, der die Vereinigung der beiden Selektoren erzwingt.<br />
Wir müssen nur noch die Selektorindizes innerhalb ihres definierenden<br />
Konstruktors und die Vereinigung ihrer Ergebnistypen in der DataDef-Relation<br />
prüfen, falls die Typen nicht identisch sind.<br />
5.3 Alternative Interaktionsabläufe<br />
Die Auswahl der einzelnen Schritte der Dialogführung ist so getroffen worden,<br />
dass sie eine möglichst breite Palette von Entscheidungsvarianten und ODL<br />
Techniken präsentiert. Der vorgestellte Ablauf stellt keinesfalls den einzig möglichen<br />
oder gar den passendsten dar. Welche Variante zum Einsatz kommt, muss<br />
im Kontext des konkreten Anwendungsszenarios und des Entwicklungsprozesses<br />
festgelegt werden.<br />
Abhängig von den Anforderungen des Einsatzgebietes und den Entwicklern<br />
muss jeweils im konkreten Fall die passende Variante der Spezifikationsunifikation<br />
ermittelt werden. Wir geben hier nur eine Übersicht, welche Aspekte einer<br />
Prüfung unterzogen werden müssen.<br />
Die ersten beiden Fragen, die abgewogen werden müssen, sind der Grad der<br />
Interaktion, d.h. welche Elemente interaktiv und welche automatisch nach bestimmten<br />
Regeln und Verfahren unifziert werden, und die Reihenfolge, in der die<br />
Einzeldialoge zur Bearbeitung angeboten werden. Wir können zum Beispiel die<br />
Entscheidungen in Bezug auf die Datensicht nach vorne ziehen und diese nach<br />
der Schnittstellenunifikation interaktiv durch den Nutzer durchführen lassen.<br />
Als Folge würden die Entscheidungen über die Unifikation von Zuständen und<br />
Transitionen durch die dann bereits festgelegte Datensicht beschränkt werden.<br />
Zu Demonstrationszwecken wählten wir hier jedoch die automatisierte Variante<br />
für die Datentypdefinitionen.<br />
Wie bereits angesprochen, gibt es im Bereich der Datentypdefinitionen unterschiedliche<br />
Varianten die Konstruktoren und Selektoren zu vereinigen, insbesondere<br />
wenn wir die entsprechenden Unifikationsrelationen automatisch berechnen<br />
wollen. Neben dieser Betrachtung ist auch Einbeziehung der Frage, inwieweit<br />
Selektoren in den Termen auftreten, wichtig, da bei einfachen Komponenten<br />
häufig auch nur einfache Datentypen (also nur parameterlose Konstruktoren)<br />
auftreten. Dies hat zur Folge, dass die Selektorunifikation nicht mehr notwendig<br />
ist und der Gesamtablauf somit beschleunigt werden kann. Die Beantwortung<br />
dieser Frage kann aber nur im konkreten Entwicklungsumfeld erfolgen.<br />
Neben der Reihenfolge der Einzelschritte der Dialogführung können auch<br />
<strong>and</strong>ere Definitionen der Unifikationsrelation zum Einsatz kommen, wie wir in<br />
Abschnitt 4.2 gesehen haben. Entsprechende Anpassungen in der Dialogführung<br />
und den Einzeldialogen, sowie dem Konstruktionsteil, sind hierfür notwendig,<br />
was wir hier nur anmerken wollen.<br />
Es sei noch einmal hervorgehoben, dass ein konkreter Interaktionsablauf an<br />
einen Entwicklungsprozess angepasst werden muss. Wir konzentrieren uns hier<br />
auf die Darstellung der derzeitigen Fähigkeiten und Möglichkeiten von ODL,<br />
noch nicht jedoch mit der Evaluierung in einem spezifischen Entwicklungsprozess.<br />
47
5.4 Umsetzung der Dialogführung in ODL<br />
Nachdem wir nun den Ablauf der Dialogführung illustriert und beschrieben,<br />
und die für das Ablaufdesign relevanten Fragen skizziert haben, wenden wir uns<br />
nun der technischen Seite zu. In diesem Abschnitt werden wir die Dialogschritte<br />
als ODL Ausdrücke formulieren. Dabei konzentrieren wir uns hier nur auf die<br />
Besonderheiten ausgewählter Schrittes. Das komplette Programmlisting findet<br />
sich im Anhang A. Es ist gegenüber den hier gemachten Ausführungen noch<br />
erweitert worden, damit es zu dem Beispiel aus Kapitel 2 passt.<br />
5.4.1 Komponentendialog<br />
Der Komponentendialog ist durch einen sehr einfachen ODL-Ausdruck definiert:<br />
context component_pair:(one:Component, two:Component) .<br />
Wir erhalten vom Benutzer ein 2-Tupel von Komponenten, dessen Elemente<br />
wir über die Tupelselektoren one und two erreichen. Diese Konvention für Tupelselektoren<br />
werden wir im Folgenden auch für die <strong>and</strong>eren Datenstrukturen<br />
beibehalten.<br />
5.4.2 Vorberechnung für Schnittstellendialog<br />
Für den Dialog zur Vereinigung der Schnittstellen (Port-Elemente) führen wir<br />
eine Vorberechnung durch. Dazu definieren wir zunächst ein Prädikat, das die<br />
externe Abhängigkeit von Port zu Component und die interne Abhängigkeit in<br />
Bezug auf das Direction-Attribut, wie in Kapitel 4 gezeigt, kapselt.<br />
define Consistent_Component_Port(<br />
component_pair:(one:Component, two:Component),<br />
pm_elem:(one:Port, two:Port)<br />
) := (<br />
pm_elem.one.Direction.IsEntry =<br />
pm_elem.two.Direction.IsEntry <strong>and</strong><br />
component_pair.one = pm_elem.one.Component <strong>and</strong><br />
component_pair.two = pm_elem.two.Component<br />
) .<br />
Mit diesem Prädikat können wir nun alle Kombinationen von Port-Elementen<br />
der zwei gewählten Komponenten auf Vereinbarkeit mit den Abhängigkeiten<br />
prüfen. Wir setzen hier den Fixpunktoperator ein, um die entsprechenden Tupel<br />
in der Menge port map possible aufzusammeln.<br />
exists port_map_possible: lfp FP1 set<br />
fp1_it:(one:(component_pair.one.Ports),<br />
two:(component_pair.two.Ports)<br />
) with (<br />
call Consistent_Component_Port(component_pair, fp1_it)<br />
) .<br />
48
Nach den Vorberechnungen lassen wir den Benutzer nun eine Teilmenge der<br />
eben berechneten Menge port map possible auswählen. Durch das Beschränkungsprädikat<br />
erzwingen wir, dass die somit nutzerdefinierte Unifikationsrelation<br />
für die Schnittstelle, wie in Abschnitt 4.2 festgelegt, eine partielle ein-eindeutige<br />
Funktion darstellt. Wie dort beschrieben kann diese Forderung entfallen, was<br />
aber zur Folge hat, dass die Konstruktion der neuen Komponente entsprechend<br />
angepasst werden muss.<br />
context port_map:{pm_test:set port_map_possible |<br />
forall pm1:pm_test . forall pm2:pm_test . (<br />
(pm1.one = pm2.one) equiv (pm1.two = pm2.two)<br />
)<br />
} .<br />
Die Ein-Eindeutigkeitsforderung werden wir im Folgenden durch den Ausdruck<br />
call Unique(XYZmap) abkürzen, wobei XYZmap durch die jeweils gemeinte<br />
Unifikationsrelation ersetzt wird. Die Abkürzung dient hier nur der Übersichtlichkeit<br />
und ist im Programmlisting im Anhang als korrekter ODL-Ausdruck<br />
jeweils ausprogrammiert 1 .<br />
5.4.3 Dialog für Zustände<br />
Der Dialog für die Zust<strong>and</strong>sunifikation erlaubt dem Benutzer ohne Vorberechnungen<br />
die Vereinigung der State-Elemente festzulegen. Durch die Beschränkung<br />
fordern wir zum einen wieder die Ein-Eindeutigkeit, zum <strong>and</strong>eren die Tatsache,<br />
dass mindestens ein Zust<strong>and</strong> der beiden Automaten unifiziert sein muss,<br />
damit im Ergebnis zumindest ein zusammenhängender Automat entsteht.<br />
context state_map:{<br />
sm_test:set (<br />
one:(component_pair.one.Automaton.State.SubStates),<br />
two:(component_pair.two.Automaton.State.SubStates)<br />
) |<br />
call Unique(state_map) <strong>and</strong> exists sm0:sm_test . true<br />
} .<br />
Wir haben an dieser Stelle von Einbeziehung der internen Abhängigkeit bei<br />
Zuständen, nämlich die Unterscheidung nach der Art des Zust<strong>and</strong>es (Start-,<br />
Zwischen- und Endzust<strong>and</strong>), abgesehen.<br />
5.4.4 Berechnung der Ein-/Ausgabevereinigungen<br />
Die Berechnung der Unifikationsrelationen für Input und Output realisieren wir<br />
in dem wir die externen Abhängigkeiten zu Transition und Port nutzen. Das<br />
Codebeispiel zeigt den Fall der Eingabeausdrücke.<br />
exists input_map: lfp FP3 set fp3_it:(one:Input, two:Input)<br />
with (<br />
exists tm_fp3:transition_map . (<br />
1 ODL fehlen zur Zeit noch die Mechanismen und Konzepte polymorphe Prädikate für<br />
Eindeutigkeit, Bijektivität, etc. zu formulieren<br />
49
tm_fp3.one = fp3_it.one.TransitionSegment <strong>and</strong><br />
tm_fp3.two = fp3_it.two.TransitionSegment<br />
) <strong>and</strong><br />
exists pm_fp3:port_map . (<br />
fp3_it.one.Port.Model = pm_fp3.one <strong>and</strong><br />
fp3_it.two.Port.Model = pm_fp3.two<br />
)) .<br />
Die so berechneten Vereinigungen werden wir sogleich einsetzen um die notwendigen<br />
Vereinigungen von Konstruktoren zu berechnen.<br />
5.4.5 Unifikation der Datendefinitionen<br />
Als letzten Teil setzen wir die Unifikation der Datendefinitionen in ODL um. Wir<br />
werden dazu die Ein-/Ausgaberelationen verwenden. Für deren Terme führen<br />
wir eine Strukturunifikation, ähnlich der prädikatenlogischen Unifikation, wie<br />
wir sie in Kapitel 3 gesehen haben, durch. Wie in Abschnitt 2.5 festgelegt bestehen<br />
Terme nur aus Konstruktoren, die aus Datendefinitionen stammen. Wir<br />
beginnen daher die Termunifikation mit ihnen.<br />
Berechnung der Konstruktorenvereinigungen<br />
Für die Vereinigung der Konstruktoren sammeln wir zunächst alle Paare von<br />
Termelementen (Appl), die Funktionsapplikationen von Konstruktoren darstellen<br />
auf. Diese Paare erhalten wir entweder direkt aus den Termen der unifizierten<br />
Ein- bzw. Ausgabeausdrücke (Input/Output) oder rekursiv als Parameter<br />
aus bereits gefundenen Paaren. Deshalb verwenden wir einen Fixpunktausdruck<br />
um diese rekursive Suche durchzuführen. Die resultierende Menge stellt<br />
gewissermaßen wieder eine Unifikationsrelation dar, denn sie enthält die Paare<br />
von Appl-Elementen, die durch die Termstruktur und die unifizierten Ein-<br />
/Ausgabeausdrücke zuein<strong>and</strong>er korrespondieren.<br />
exists appl_fixpoint: lfp FP_Appl set<br />
fpappl_it:(one:Appl, two:Appl)<br />
with (<br />
exists im_fpa:input_map . (<br />
im_fpa.one.Pattern.Model = fpappl_it.one <strong>and</strong><br />
im_fpa.one.Pattern.Model = fpappl_it.two<br />
) or<br />
exists om_fpa:output_map . (<br />
om_fpa.one.Expression.Model = fpappl_it.one <strong>and</strong><br />
om_fpa.one.Expression.Model = fpappl_it.two<br />
) or<br />
exists fpa_elem:FP_Appl . (<br />
is Args(fpa_elem.one, fpappl_it.one) <strong>and</strong><br />
is Args(fpa_elem.two, fpappl_it.two) <strong>and</strong><br />
indexOf(fpappl_it.one, fpa_elem.one, "Args") =<br />
indexOf(fpappl_it.two, fpa_elem.two, "Args")<br />
)<br />
) .<br />
50
Konstruktoren können in den Applikationselementen an zwei Stellen auftreten:<br />
als applizierte Funktion (Head-Assoziation) oder als ein Parameter (Args-<br />
Assoziation). Im zweiten Fall muss natürlich zusätzlich der Index des Parameters<br />
beachtet werden.<br />
Mit dem folgenden Prädikat überprüfen wir ein Paar von Applikationen und<br />
ein Paar von Konstruktoren, ob die Unifikation der Konstruktoren durch die<br />
strukturelle Übereinstimmung der Applikationen impliziert wird.<br />
define testApplForConstructor(<br />
appl:(one:Appl, two:Appl),<br />
con:(one:Constructor, two:Constructor)<br />
) := (<br />
/* applied function case */<br />
( appl.one.Head = con.one <strong>and</strong><br />
appl.two.Head = con.two<br />
) or<br />
/* parameter case */<br />
( is Args(appl.one, con.one) <strong>and</strong><br />
is Args(appl.two, con.two) <strong>and</strong><br />
indexOf(con.one, appl.one, "Args") =<br />
indexOf(con.two, appl.two, "Args")<br />
)<br />
) .<br />
Mit dieser Vorberechnung der Applikationselemente und der Definition des<br />
Testprädikats können wir nun die Vereinigungsrelation der Konstruktoren berechnen.<br />
Wir sammeln ein Paar von Konstruktoren in der Fixpunktmenge auf,<br />
wenn es, entweder durch ein Element der Ein- bzw. Ausgaberelation oder durch<br />
ein Paar von Applikationen, als zu unifizierend bestimmt wird.<br />
exists constructor_map: lfp FP4 set<br />
fp4_it:(one:Constructor, two:Constructor)<br />
with (<br />
/* single signal in input pattern */<br />
exists im_fp4:input_map . (<br />
fp4_it.one = im_fp4.one.Pattern.Model <strong>and</strong><br />
fp4_it.two = im_fp4.two.Pattern.Model<br />
)<br />
or<br />
/* single signal in output expression */<br />
exists om_fp4:output_map . (<br />
fp4_it.one = om_fp4.one.Expression.Model <strong>and</strong><br />
fp4_it.two = om_fp4.two.Expression.Model<br />
)<br />
/* unification implied by application element */<br />
or exists fp4_ap:appl_fixpoint .<br />
call testApplForConstructor(fp4_ap, fp4_it)<br />
) .<br />
Die beiden in diesem Abschnitt berechneten Unifikationsrelationen müssen<br />
noch dahingehend überprüft werden, ob die Input- und Output-Relation die<br />
51
Abhängigkeiten bezüglich dieser beiden Relationen erfüllen. Wir wissen zwar,<br />
dass aufgrund der Konstruktion der beiden Relationen für jedes Konstruktor<br />
bzw. Applikationspaar ein Ein- oder Ausgabepaar existiert, nicht jedoch ob für<br />
alle Ein-/Ausgabepaare die Terme unifiziert werden konnten. Wir müssen also<br />
überprüfen, ob es für alle Ein- und Ausgabeterme entsprechende Konstruktoroder<br />
Applikationsunifikationen gibt. Da Konstruktorenpaare auch durch Applikationspaare<br />
entst<strong>and</strong>en sein können, sind auch letztere in Bezug auf erstere<br />
zu prüfen. Nur dadurch können wir sicherstellen, dass die unifizierten Terme<br />
strukturäquivalent sind.<br />
Ein Beispiel soll Notwendigkeit dieser Zusatzprüfungen verdeutlichen. Abbildung<br />
5.5 zeigt die Termstruktur von drei Eingabeausdrücken. Die erste und<br />
der zweite Term, sowie der erste und der dritte, können nicht unifiziert werden,<br />
weil die obersten Elemente der Baumdarstellung ein Konstruktor und eine<br />
Applikation sind. Hierfür reicht die Überprüfung der Abhängigkeit zwischen<br />
Eingaberelation und Konstruktorrelation.<br />
p?Con1<br />
q?Con2(Con3)<br />
r?Con4(Con5(Con6))<br />
Con1<br />
Appl<br />
Appl<br />
Con2<br />
Con3<br />
Con4<br />
Appl<br />
Head<br />
Args<br />
Con5<br />
Con6<br />
Abbildung 5.5: Nicht-unifizierbare Termstrukturen<br />
Dass der zweite Term und dritte Term nicht unifiziert werden können, kann<br />
allerdings nur durch die Überprüfung der Applikationsrelation bewiesen werden,<br />
denn die Eingaberelation bezieht die Strukturäquivalenz nur auf das oberste<br />
Element des Terms. Bei der Überprüfung der Applikationsrelation tritt dann<br />
der Fehler im ersten Parameter zutage.<br />
Die genannten Abhängigkeiten können mit dem aus Kapitel 4 bekannten<br />
ODL St<strong>and</strong>ardausdrücken überprüft werden, weshalb wir hier auf eine Wiederholung<br />
des ODL Codes verzichten.<br />
Im Hinblick auf das Konstruktionsverfahren fordern wir wieder die Ein-Eindeutigkeit<br />
von den berechneten Relationen.<br />
Berechnung der Datentypvereinigungen<br />
Vorteil der verherigen umfangreichen Berechnung ist, dass wir nun die Datentyprelation<br />
sehr leicht ableiten können. Zwei Datentypdefinitionen sind zu unifizieren,<br />
wenn es ein Paar von vereinigten Schnittstellen oder vereinigten Konstruktoren<br />
gibt. Dies können wir, wie schon bekannt, durch die St<strong>and</strong>ardausdrücke<br />
für Abhängigkeiten bewerkstelligen.<br />
exists typedef_map:lfp FP_TDef set<br />
fp_tdef_it:(one:DataDef, two:DataDef)<br />
with (<br />
52
call Consistent_Port_TypeDef(port_map, fp_tdef_it) or<br />
call Consistent_Const_TypeDef(constructor_map, fp_tdef_it)<br />
) .<br />
Strukturunifkation der Selektoren<br />
Auch für die Selektorunifikation ist durch die obige Berechnung der Konstruktorrelation<br />
die meiste Arbeit erledigt. Wir müssen lediglich die indexgleichen<br />
Selektorenpaare für die Konstruktorenpaare zusammensuchen. Anders gesagt,<br />
es werden zwei Selektoren unifiziert, wenn die definierenden Konstruktoren vereinigt<br />
sind und sie den gleichen Index haben.<br />
exists selector_map: lfp FP6 set<br />
fp6_it:(one:Selector, two:Selector)<br />
with (<br />
exists cm_fp6:constructor_map . (<br />
fp6_it.one.Constructor = cm_fp6.one <strong>and</strong><br />
fp6_it.two.Constructor = cm_fp6.two <strong>and</strong><br />
indexOf(fp6_it.one, cm_fp6.one, "Selectors") =<br />
indexOf(fp6_it.two, cm_fp6.two, "Selectors")<br />
))) .<br />
5.4.6 Zusammenfassung<br />
In Abschnitt 5.4 haben wir gesehen, wie die Unifikationsrelationen im Bereich<br />
der Datensicht berechnet wurden. Wir nutzten dafür ein Unifikationsverfahren<br />
für die Terme der Ein-/Ausgabeelemente, das auf der Struktur der Terme beruht.<br />
Wie für die Eingaben des Nutzers in interaktiven Dialogen, mussten auch<br />
die berechneten Ergebnisse auf Konformität mit dem im Folgenden erläuterten<br />
Konstruktionsteil der Spezifikationsvereinigung überprüft werden.<br />
5.5 Konstruktion der Vereinigungskomponente<br />
Bisher haben wir nur den analytischen Teil der Spezifikationsvereinigung betrachtet.<br />
Wir haben geeignete Datenstrukturen, die Unifikationsrelationen, in<br />
ODL definiert und erzeugen durch Nutzerinteraktion konkrete Datenobjekte.<br />
Wir werden diese nun nutzen, um aus den dort enthaltenen Informationen das<br />
Ergebnis der Spezifikationsunifikation zu erstellen, wie wir als erstes erläutern<br />
werden. Wir geben dann zunächst eine kurze Einführung in die konstruktiven<br />
Fähigkeiten von ODL, d.h. das Erzeugen von neuen Elementen und das Erstellen<br />
bzw. Auflösen von Assoziationen im Modell. Danach greifen wir einige Teile<br />
des Konstruktionsverfahrens heraus und erläutern sie. Wir besprechen nicht den<br />
gesamten Konstruktionsausdruck, da er in weiten Teil strukturell identisch ist<br />
und sich nur auf unterschiedliche Teile des Modells bezieht.<br />
5.5.1 Konstruktionsprinzip<br />
Die Konstruktion der verschmolzenen Komponente folgt für die einzelnen Modellelemente<br />
immer dem gleichen Prinzip. Dieses Prinzip ist bis auf die verwen-<br />
53
deten Quellstrukturen identisch zum Vorgehen beim Kopieren einer Komponentenhierarchie,<br />
wie sie in [Höl05] entwickelt wurde.<br />
Jede Instanz einer Unifikationsrelation definiert für uns eine ein-eindeutige<br />
Abbildung zwischen zu unifzierenden Elementmengen. Für konstruieren daher<br />
für jedes Element einer konkreten Unifikationsrelation ein entsprechendes neues<br />
Modellelement, wobei wir uns zusätzlich eine Tabelle erstellen, die das unifizierte<br />
Tupel mit dem neuen Element verknüpft.<br />
Anschließend analysieren wir für jede Modellelementklasse die relevanten<br />
Assoziationen, wie sie in den Ausgangsspezifikationen existieren, und bauen die<br />
entsprechende Struktur mit den neu erzeugten Elementen nach.<br />
Das Konstruktionsverfahren muss dabei auch beachten, dass es nicht nur<br />
unifzierte Modellelemente, sondern auch kopierte Elemente gibt. Der Strukturaufbau<br />
wird diese Unterscheidung entsprechend beachten. Wir konstruieren<br />
hierfür ebenfalls eine Tabelle, die die kopierten Elemente in Beziehung zu neuen<br />
Elementen setzt. Wir führen dies in Abschnitt 5.5.5 vor.<br />
Bezieht sich beispielsweise eine Eingabe im Quellmodell auf einen unifzierten<br />
Port, so verknüpfen wir das das neue Eingabeelement mit dem entsprechenden<br />
neuen Portelement aus der oben als erstes genannten Tabelle, <strong>and</strong>ernfalls suchen<br />
wir uns das Portelement aus der Tabelle für kopierte Portelemente.<br />
5.5.2 Erzeugung neuer Modellelemente mit ODL<br />
Zur Erzeugung von neuen Modellelementen stellt die ODL im Wesentlichen zwei<br />
Mechanismen bereit. Einzelne neue Elemente können durch den Typmodifikator<br />
new in Verbindung mit einem Existenzquantor erzeugt werden. Ein Beispiel<br />
hierfür sehen wir gleich, wenn wir für die beiden zu vereinigenden Komponenten<br />
eine neue erzeugen werden. Neu erzeugte Elemente müssen mit entsprechenden<br />
”result has”-Anweisungen an das bestehende Modell gekoppelt werden, da sie<br />
sonst am Ende der Ausführung wieder verworfen werden 2 .<br />
Das Erzeugen von einzelnen Modellelementen ist allerdings häufig nicht ausreichend<br />
um komplexere Modellstrukturen anzulegen. Im Beispiel ist dies der<br />
Fall, wenn wir die Eingabeausdrücke (Input) konstruieren. Wir benötigen dazu<br />
gleichzeitig Zugriff auf alle neu erzeugten Schnittstellenelemente (Port) und alle<br />
neu erzeugten Konstruktoren (Constructor). Ein ähnlich gelagerter Fall wurde<br />
bereist in [Höl05] erkannt und durch den speziellen ODL-Typ map für Abbildungen<br />
gelöst.<br />
Der Abbildungstyp map dient uns dazu, eine Zuordnungstabelle zu erstellen,<br />
die jeweils einem Element einer Unifikationsrelation bijektiv ein neu erzeugtes<br />
Element gleicher Modellelementklasse zuordnet. Wir haben damit gleichzeitig<br />
Zugriff auf alle neuen Elemente diesen Typs und können sie über die Bijektivität<br />
der Abbilung auch eindeutig identifizieren. Die genaue Funktionsweise des<br />
Abbildungstyps in ODL wird in [Höl05] ausführlich erläutert. Im übernächsten<br />
Abschnitt zeigen wir den Einsatz von map anh<strong>and</strong> der Konstruktion der Schnittstellenobjekte.<br />
2 Sie gelten wie bestehende Elemente, deren Assoziationen mit result not has entfernt<br />
wurden, als gelöscht.<br />
54
5.5.3 Konstruktion der Komponente<br />
Da wir im interaktiven Ablauf nur ein Paar von Komponenten betrachten, gestaltet<br />
sich das Generieren der Vereinigungskomponente denkbar einfach.<br />
exists new_comp: new Component . (<br />
context new_comp_name:String .<br />
result has Name(new_comp, new_comp_name)<br />
<strong>and</strong> result has<br />
SubComponents(component_pair.one.SuperComponent, new_comp)<br />
<strong>and</strong> ...<br />
Über den Namen der neuen Komponente lassen wir den Nutzer durch den<br />
entsprechenden context-Ausdruck entscheiden. Die neue Komponente wird anschließend<br />
parallel zur ersten unifzierten Komponente in das Modell eingefügt.<br />
5.5.4 Konstruktion der Schnittstellenelemente<br />
Für die Konstruktion der Port-Elemente benutzen wir den map-Operator. Wir erzeugen<br />
eine bijektive Abbildung, sodass zu jedem Tupel von unifizierten Schnittstellenelementen<br />
genau ein neues Element entsteht.<br />
exists new_port_map:map orig:port_map to copy:new Port . (<br />
/* bijective mapping */<br />
forall npm1:new_port_map . forall npm2:new_port_map . (<br />
npm1.orig = npm2.orig equiv npm1.copy = npm2.copy<br />
) <strong>and</strong><br />
/* build associations */<br />
run makePorts(new_comp, new_port_map, new_tconst_map) <strong>and</strong><br />
...<br />
Die Anbindung und Verknüpfung der neuen Elemente haben wir hier der<br />
Übersichtlichkeit halber in ein Unterprogramm ausgelagert. Hier wird für jedes<br />
neue Schnittstellenelement die Anbindung an die neue Komponente, die Namensgebung<br />
und das Setzen der Port-Attribute durchgeführt. Im Gesamtablauf<br />
müssen wir die Datentypdefinitionen bereits generiert haben, da wir sie, wie im<br />
folgenden Programmausschnitt ersichtlich, korrekt verknüpfen müssen.<br />
define makePorts(<br />
new_comp:Component,<br />
new_port_map:set(orig:(one:Port, two:Port),copy:Port),<br />
new_tconst_map:set (<br />
orig:(one:DataDef, two:DataDef),<br />
copy:AbstractType<br />
)<br />
) as (<br />
forall mk_prt:new_port_map . (<br />
/* link port to new component */<br />
result has Ports(new_comp, mk_prt.copy) <strong>and</strong><br />
/* generate name for the port */<br />
55
esult has Name(mk_prt.copy,<br />
concat(mk_prt.orig.one.Name, mk_prt.orig.two.Name)) <strong>and</strong><br />
/* set direction attribute */<br />
exists mk_nd:new Direction . (<br />
result has Direction(mk_prt.copy, mk_nd) <strong>and</strong><br />
result has<br />
IsEntry(mk_nd, mk_prt.orig.one.Direction.IsEntry)<br />
) <strong>and</strong><br />
/* set port type */<br />
exists mk_prt_mt:new MIFType . (<br />
result has DefinedType(mk_prt.copy, mk_prt_mt) <strong>and</strong><br />
exists ntm_elem_port:new_tconst_map . (<br />
mk_prt.orig.one.DefinedType.Model =<br />
ntm_elem_port.orig.one.TConst <strong>and</strong><br />
mk_prt.orig.two.DefinedType.Model =<br />
ntm_elem_port.orig.two.TConst <strong>and</strong><br />
result has Model(mk_prt_mt, ntm_elem_port.copy)<br />
)))) .<br />
Hier sehen wir auch, dass bei vielen Modellelementen zusätzliche Elemente<br />
erzeugt werden müssen, die meist nur als Kapselobjekte für primitive Datentypen<br />
(Direction) oder zur Flexibilisierung der Modellierungsfähigkeiten (MIFType<br />
für unvollständige Modelle) von AutoFocus 2 benutzt werden.<br />
5.5.5 Konstruktion nicht-unifizierter Elemente<br />
Da die Unifikationsrelationen hier partielle Funktionen sein dürfen, müssen wir<br />
nicht-unifizierte Elemente der jeweiligen Modellelementklasse ebenfalls beh<strong>and</strong>eln.<br />
Für sie erstellen wir jeweils eine Kopie und verknüpfen sie entsprechend<br />
mit den restlichen neuen Elementen. Hier zeigen wir die Umsetzung wieder am<br />
Beispiel der Port-Elemente.<br />
Wir konstruieren dazu zunächst die Menge der nicht-unifizierten Port-Elemente.<br />
Sie besteht aus allen Elementen, die zu einer der beiden Komponenten<br />
gehören und nicht in der portmap-Menge auftauchen.<br />
exists copy_port_set: lfp FP7 set fp7_it:Port<br />
with (<br />
(<br />
fp7_it.Component = component_pair.one or<br />
fp7_it.Component = component_pair.two<br />
) <strong>and</strong><br />
neg exists sm_fp7:port_map . (<br />
sm_fp7.one = fp7_it or<br />
sm_fp7.two = fp7_it<br />
)<br />
).<br />
Für diese Menge erzeugen, wie eben, eine bijektive Abbildung auf neue Port-<br />
Elemente und bauen für diese die Assoziationen mit einer Subroutine auf, die<br />
56
im Wesentlichen makePorts() entspricht.<br />
define copyPorts(<br />
new_comp:Component,<br />
new_copy_port_map:set(orig:Port,copy:Port)<br />
) as (<br />
forall mk_prt:new_copy_port_map . (<br />
result has Ports(new_comp, mk_prt.copy) <strong>and</strong><br />
result has Name(mk_prt.copy, mk_prt.orig.Name) <strong>and</strong><br />
exists mk_nd:new Direction . (<br />
result has Direction(mk_prt.copy, mk_nd) <strong>and</strong><br />
result has IsEntry(mk_nd, mk_prt.orig.Direction.IsEntry)<br />
) <strong>and</strong><br />
exists mk_prt_mt:new MIFType . (<br />
result has DefinedType(mk_prt.copy, mk_prt_mt) <strong>and</strong><br />
result has Model(mk_prt_mt, mk_prt.orig.DefinedType.Model)<br />
))) .<br />
5.6 Zusammenfassung<br />
In diesem Kapitel haben wir einen möglichen interaktiven Ablauf der Spezifikationvereinigung<br />
illustriert und in ODL umgesetzt. Unser Ziel war die Darstellung<br />
der Fähigkeiten von ODL zur Definition interaktiver Modelltransformationen.<br />
Für solch komplexe Transformationen gibt es meist mehrere alternative<br />
Dialogführungen und Dialogformen, sodass eine genaue Betrachtung im jeweils<br />
konkreten Fall notwendig ist.<br />
Wir haben die Spezifikationsunifikation in zwei wesentliche Teile gegliedert.<br />
Der analytische Teil lieferte interaktiv oder, wie im Falle der Datentypen und der<br />
Termunifikation, automatisch berechnete Datenstrukturen für die Unifikationsrelationen.<br />
Wir haben auf die Darstellung der Berechnung für nicht-unifzierte<br />
Elemente, d.h. Elemente, die in die erzeugte Spezifikation nur einkopiert werden<br />
müssen, verzichtet. Der Leser sei hierfür auf den Programmtext in Anhang A<br />
verwiesen.<br />
Diese vom Nutzer eingegebenen oder berechneten Daten bilden, so sie konsistent<br />
sind, die Grundlage für den konstruktiven Teil. Er erzeugt die vereinigte<br />
Komponente. Wir haben gesehen, dass beide Teile aufein<strong>and</strong>er abgestimmt sein<br />
müssen, wobei die Forderung der Ein-Eindeutigkeit hier wesentliche Vereinfachungen<br />
gebracht hat. Unsere Darstellungen begrenzten wir auf die Vorführung<br />
der wichtigsten Teile. Das vollständige Programm findet sich in Anhang A.<br />
57
Kapitel 6<br />
Fazit und Ausblick<br />
Abschließend unterziehen wir die in dieser Arbeit dargestellten Ergebnisse einer<br />
kritischen Einordnung. Danach wenden wir uns dem Ausblick zu und geben<br />
einige motivierende Ideen für weiterführende Betrachtungen und Arbeiten.<br />
6.1 Einordnung dieser Arbeit<br />
Diese Arbeit befasste sich mit komplexen Modellanalysen und -transformationen<br />
in ODL und AutoFocus 2. Vorangegangene Arbeiten für ODL dienten bisher<br />
dem Zweck die Transformationssprache mit zusätzlichen Fähigkeiten, wie<br />
der interaktiven Schnittstelle ([Tra03]) und der Fixpunktmengenkonstruktion<br />
([Höl05]), zu erweitern. Somit stellt die vorliegende Arbeit einen ersten Konzeptnachweis<br />
dar, dass sich mit ODL auch umfangreichere, interaktive Transformationen<br />
konstruieren lassen.<br />
Das Thema Unifikation war für unsere Beispielmodelltransformation unser<br />
roter Faden . Wir haben es an mehreren Stellen in verschiedenen Formen angetroffen.<br />
Zunächst haben wir die prädikatenlogischen Unifikation auf die Datentypdefinitionen<br />
von AutoFocus 2 übertragen, wobei wir hier als Äquivalenzmerkmal<br />
die Namensgleichheit verwendet haben. Im nächsten Schritt erarbeiteten<br />
wir uns ein Konzept für die Vereinigung von Spezifikationen, das zusätzlich<br />
zur Datensicht noch die Struktur- und Verhaltenssicht von AutoFocus 2-Komponenten<br />
miteinbezog. Als Teil des interaktiven Ablaufs haben wir schließlich<br />
noch die Termunifikation beh<strong>and</strong>elt.<br />
Als Fazit dieser Arbeit lässt sich sagen, dass ODL ein mächtiges Werkzeug<br />
für Definition von Modelltransformationen ist, allerdings mit einer hohen Einstiegsbarriere,<br />
da komplexere Transformationen sehr schnell zu umfangreichen<br />
ODL Ausdrücken führen. Im Hinblick auf Verständlichkeit haben regel- oder<br />
musterbasierte Ansätze für Modelltransformation, wie sie im Umfeld der UML<br />
und der MDA zur Zeit verbreitet sind, Vorteile. Diese werden allerdings durch<br />
die Ausdrucksmächtigkeit bei komplexen Transformationen, insbesondere solche<br />
mit rekursiven Strukturen und Fixpunktberechnungen, wettgemacht. Der Übergang<br />
zu interaktiven Modelltransformationen ist dann hilfreich, wenn manche<br />
Datenstrukturen, wie in Kapitel 4 sehr große Suchräume erzeugen.<br />
Es hat sich gezeigt, dass die Fundierung von ODL als ein relationaler Kalkül<br />
sehr wertvoll ist, da Konzepte, wie die Abhängigkeiten und Unifikationsrela-<br />
58
tionen aus Kapitel 4, schnell und leicht umsetzbar sind. Die enge Beziehung<br />
von ODL zur Prädikatenlogik erleichtert zudem den Nachweis der Korrektheit<br />
von Transformationen und die Integration in Entwicklungsabläufe, da Vor- und<br />
Nachbedingungen ebenfalls mit ODL formuliert und für Modelle überprüft werden<br />
können.<br />
Es zeigten sich im Verlauf dieser Diplomarbeit aber auch kleine Schwachstellen<br />
von ODL, zu denen wir im nächsten Abschnitt Denkansätze und Vorschläge<br />
machen.<br />
6.2 Ansätze zur Weiterentwicklung der ODL<br />
Im Verlauf dieser Arbeit zeigten sich einige Verbesserungsmöglichkeiten für die<br />
Transformationssprache ODL. Insbesondere im Hinblick auf die nachfolgend<br />
dargelegte Möglichkeit ODL auf <strong>and</strong>ere Modellierungssprachen zu übertragen,<br />
sollten noch weitergehende Konzepte integriert werden.<br />
Folgende Ansätze und Erweiterungen scheinen sinnvoll:<br />
• Die Nutzerschnittstelle wird bei komplexeren Strukturen schnell unübersichtlich<br />
und wenig intuitiv. Insbesondere bei rekursiven Abfragen, wie wir<br />
sie im Kapitel 3 gesehen haben, ist eine Verbesserung wünschenswert. Es<br />
sind zeigen sich hier zwei Möglichkeiten. Zum einen könnte die Ausdehnung<br />
des context-Quantors auf Fixpunktmengen erfolgen; zum <strong>and</strong>eren<br />
könnte ein spezieller Sammlungstyp für context-Ausdrücke das Problem<br />
unh<strong>and</strong>licher, rekursiver und interaktiver Mengenkonstruktionen lösen.<br />
Beide Möglichkeiten sind dabei eingehend auf die ODL Evaluationssemantik<br />
hin zu untersuchen.<br />
• Es kann in manchen Situationen nützlich sein, eine Transformation in mehrere<br />
Einzelschritte zu unterteilen. Hierzu könnte ODL mit Konzepten für<br />
Transaktionen, wie sie im Bereich von Datenbanken schon alltäglich sind,<br />
erweitert werden, um die Zusammenfassung von einzelnen ODL Transformationen<br />
zu größeren, in der Wirkung atomaren Transformationen zu<br />
ermöglichen.<br />
• Als langfristige Evolution ist die Untersuchung und Integration von Techniken<br />
<strong>and</strong>erer Modelltransformationsansätze, insbesondere aus dem Bereich<br />
der leichter zu erlernenden regel- und musterbasierten Ansätze, ein<br />
Ziel, dass für ODL eine weitere Leistungssteigerung bringen kann.<br />
Bevor diese Erweiterungen umgesetzt werden, sollte allerdings das Ziel sein,<br />
die bisherigen Einzelarbeiten zu einer Kernversion der ODL zusammenführen<br />
und zu optimieren. Parallel dazu scheint ein ODL Tutorialdokument sinnvoll,<br />
um einerseits den Einstieg für Interessierte zu erleichtern und den aktuellen<br />
Entwicklungsst<strong>and</strong> der ODL zu dokumentieren.<br />
6.3 ODL für <strong>and</strong>ere Modellierungssprachen und<br />
-werkzeuge<br />
Der Einsatz von ODL für Transformationen in <strong>and</strong>eren Modellierungssprache ist<br />
sowohl durch die mengenorientierte Sichtweise prinzipiell möglich, wie auch tech-<br />
59
nisch umsetzbar, da die bisherigen Arbeiten an der Implementierung eine klare<br />
Schnittstelle zum verwendeten Metamodell nutzen. Die Hauptarbeit zur Anpassung<br />
von ODL an weitere Entwicklungswerkzeuge betrifft diese Schnittstelle.<br />
Wir werden die Portierung anh<strong>and</strong> der Unified Modeling Language (UML)<br />
motivieren.<br />
Wie wir gesehen haben, ist ODL als Transformationssprache für Modelle mit<br />
diesen über das Metamodell verbunden. ODL ist im Falle von AutoFocus 2 über<br />
eine generische Schnittstelle mit dem AutoFocus 2-Metamodell gekoppelt, d.h.<br />
die durch das Metamodell vorgegebenen Assoziationen zwischen Modellelementen<br />
werden zur Laufzeit ermittelt. Sie sind also nicht fester Best<strong>and</strong>teil des ODL-<br />
Moduls. Diese lose Bindung zwischen ODL und dem Metamodell ermöglicht eine<br />
leichte Adaption der Transformationssprache für <strong>and</strong>ere Modellierungssprachen.<br />
In [HS05] wird neben der hier vorgestellten Modelltransformation für AutoFocus<br />
2, auch ein Beispiel für eine Modelltransformation für UML-Modelle<br />
gegebenen. Sowohl die AutoFocus 2-Modellierungssprache als auch die UML<br />
(vgl. [OMG03a]) sind mit Hilfe eines Metamodells beschrieben. Beide Metamodelle<br />
können dabei als Instanzen eines Meta-Metamodells angesehen werden, wie<br />
es die MetaData Architektur in der MetaObject Facility Specification [OMG04]<br />
definiert. Diese Ähnlichkeit der Metamodelle ermöglicht es, dass ODL auch als<br />
Transformationssprache in UML-Werkzeuge integriert werden kann, wenn die<br />
entsprechende ODL-Metamodell-Schnittstelle angepasst wird.<br />
Die Integration von ODL in UML-Werkzeuge könnte dabei ohne große Zusatzarbeiten<br />
durchgeführt werden, weil in einem Systementwicklungsprojekt am<br />
Lehrstuhl eine einheitliche Schnittstelle zu mehreren UML-Werkzeugen (Rational<br />
Rose Enterprise 2003, argoUML 0.16.1, . . . ) geschaffen wurde (vgl. [Dun05]).<br />
Dennis Dungs hat hierbei für seine Schnittstelle eine Implementierung des<br />
UML Metamodells geschaffen. Diese ist im Gegensatz zum AutoFocus 2-Metamodell<br />
allerdings nicht zur Laufzeit als Instanz des Meta-Metamodells vorh<strong>and</strong>en.<br />
Die Kopplung von ODL über diese generische Schnittstelle sollte aber<br />
möglich sein und daher einer genaueren Betrachtung, möglicherweise in Form<br />
eines Systementwicklungsprojekts, unterzogen werden.<br />
Nach einer ersten Analyse zeigten sich folgende Arbeitspakete:<br />
• Spezifikation und Implementierung der Schnittstelle zwischen ODL und<br />
dem UML Metamodell, wobei die analytischen Fähigkeiten von Java (Reflection-API)<br />
Anwendung finden könnten um die fehlende Beschreibung<br />
der Meta-Metamodell-Instanz zu ersetzen. Alternativ kann das Metamodell<br />
möglicherweise auch mit ArgoUML reverse-engineered werden, um<br />
dann mit dem in AutoFocus 2 verwendeten MMGen-Verfahren eine Laufzeitinstanz<br />
des Metamodells zu erhalten, was die Kopplung von ODL erheblich<br />
vereinfachen würde.<br />
• Nutzung der UML Schnittstelle zur Integration des ODL Editors in die<br />
UML Werkzeuge. Besonderes Augenmerk richtet sich hier auf die Integration<br />
in den GUI des jeweiligen UML-Werkzeugs.<br />
• Nachweis der Funktionsfähigkeit durch Umsetzung der in [HS05] beschriebenen<br />
Modelltransformation für UML Modelle (MoveUpFeature-Transformation).<br />
60
6.4 ODL-unterstützter Entwicklungsprozess<br />
Zum Abschluß betrachten wir noch kurz die Ansätze und Ziele der Parallelarbeit<br />
von David Pasch (vgl. [Pas05]). Fokus seiner Arbeit ist der Einsatz von ODL in<br />
den verschiedenen Phasen des <strong>Software</strong>entwicklungsprozesses.<br />
Wie wir gesehen haben, ist die konkrete Festlegung des Ablaufs der Spezifikationsvereinigung<br />
stark von den verwendeten Modellierungsmöglichkeiten<br />
ab, insbesondere im Bereich der Datentypen. Daher können komplexe Modelltransformationen<br />
erst in Zusammenhang mit weiteren Betrachtungen des Gesamtentwicklungsprozesses<br />
die volle Wirkung erlangen, indem sie für die jeweils<br />
betrachtete <strong>Software</strong> und die aktuelle Entwicklungsphase, als auch für die Arbeitsweise<br />
einzelner Entwickler oder -teams massgeschneidert werden.<br />
David Pasch hat sich in seiner Arbeit mit dem Problem der Prozessunterstützung<br />
befasst und AutoFocus 2 um einen entsprechenden Mechanismus<br />
erweitert. Dieser erlaubt für AutoFocus 2-Projekte die Festlegung von Entwicklungsphasen,<br />
wobei jede Phase durch entsprechende ODL Eintrittsbedingungen<br />
charakterisiert werden kann. Desweiteren ist es möglich für jede Phase<br />
ODL-Transformationen vorzudefinieren, die dem Entwickler für typische Bearbeitungsschritte<br />
des Modells in den entsprechenden Phasen zur Verfügung<br />
stehen sollen.<br />
Die Arbeit von David Pasch schlägt für ODL also die Brücke zur Evaluierung<br />
im Kontext des <strong>Software</strong> <strong>Engineering</strong>s und von Entwicklungsprozessen.<br />
Die vorliegende Arbeit zeigte eine erste sehr komplexe Modelltransformation<br />
und schaffte somit eine Grundlage für vertiefte Betrachtungen des Themas Modelltransformation.<br />
Die Relevanz und Portierbarkeit von ODL für <strong>and</strong>ere Modellierungssprachen<br />
haben wir oben motiviert, sodass insgesamt eine theoretische,<br />
wie technische Basis für weitere Betrachtungen des Themas Modelltransformation<br />
geschaffen ist.<br />
61
Anhang A<br />
ODL Programm der<br />
Spezifikationsvereinigung<br />
A.1 Anmerkungen zum Programm<br />
A.1.1<br />
Erweiterte Abhängigkeiten<br />
In diesem Anhang stellen wir die ODL-Umsetzung der Spezifikationsunifikation<br />
vor. Wir verwenden hierzu ein erweitertes Abhängigkeitsnetz, wie in Abbildung<br />
A.1 gezeigt. Neben den aus Kapitel 4 bekannten Modellelementklassen und<br />
Abhängigkeiten (letztere wieder als durchgezogene Pfeile dargestellt), finden sich<br />
nun auch die Modellelemente für lokale Variablen (LocVariable) und deren Repräsentation<br />
in Termen (LocVariableConst) , Funktionsapplikation (Appl), Selektoren,<br />
Terme und konstante Ausdrücke (Const), Transitionsverbindungspunkte<br />
(InterfacePoint), sowie Aktionen (Action) und Vorbedingungen (Condition).<br />
Desweiteren zeigt die Abbildung die Klassenhierachie des Metamodells für Terme<br />
und konstante Ausdrücke.<br />
Das bisher noch nicht beachtete Modellierungselement LocVariable für lokale<br />
Variablen unterscheidet sich nicht von den Elementen für Port. Beide haben<br />
als Attribute einen Namen und einen definierten Typ und sind mit einer n:1-<br />
Beziehung an Component geknüpft. Die Assoziation zwischen LocVariable und<br />
LocVariableConst ist eine 1:1-Beziehung, d.h. mit jeder lokalen Variablen ist ein<br />
eindeutiges Repräsentationsobjekt für die ihre Verwendung als Term vorh<strong>and</strong>en.<br />
Die gekennzeichnete Vererbungshierarchie von Term und Const bedarf einer<br />
besonderen Beh<strong>and</strong>lung. Alle Abhängigkeiten, die auf Oberklassen verweisen,<br />
müssen die Unterklassen mitberücksichtigen, d.h. die Abhängigkeit ist erfüllt,<br />
wenn es in einer der Unifikationrelationen der Unterklassenelemente ein entsprechend<br />
vereinigtes Elementtupel gibt. Der in Kapitel 4 eingeführte Formalismus<br />
zur Beschreibung einer Unifikationsrelation muss für Klassenhierarchien folgendermaßen<br />
angepasst werden:<br />
62
Component<br />
DataDefinition<br />
DefinedType.Model<br />
Port<br />
State<br />
LocVariable<br />
Port.Model<br />
LocVariable.Model<br />
(+)<br />
InterfacePoint<br />
(*)<br />
TransitionSegment<br />
Term<br />
Pattern.Model /<br />
Expression.Model<br />
Input / Output<br />
Condition<br />
Action<br />
Const<br />
Head<br />
Expression<br />
Constructor<br />
Appl<br />
Args<br />
0..*<br />
Selector<br />
LocVariableConst<br />
(*) SourcePoint und DestinationPoint<br />
(+) Selector.DefinedType = DataDef.TConst<br />
Abbildung A.1: Abhängigkeiten für erweiterte Unifikation<br />
∀ur ∈ UnifRel : (<br />
∃usc1 ∈ UnifRelSubClass1 :<br />
ur.one.AssociatedEntity = usc1.one ∧ ur.two.AssociatedEntity = usc1.two<br />
∨<br />
∃usc2 ∈ UnifRelSubClass1 :<br />
ur.one.AssociatedEntity = usc2.one ∧ ur.two.AssociatedEntity = usc2.two<br />
∨<br />
. . .)<br />
Die Relation zwischen Selector und DataDef besteht nicht in einem einzelnen<br />
Attribut, sondern hier muss die Verbindung über das entsprechende TConst-Element<br />
hergestellt werden.<br />
Die Abhängigkeit der Elemente TransitionSegment und InterfacePoint ist<br />
gestrichelt dargestellt, da hier ebenfalls eine Besonderheit gibt. Für alle unifizierten<br />
Transitionen müssen sowohl die Quellpunkte als auch die Zielpunkte<br />
unifiziert sein. Die Abhängigkeit stellt somit genau genommen zwei Abhängigkeiten<br />
dar, die beide erfüllt sein müssen.<br />
A.1.2<br />
Problembeschränkungen<br />
Für die bisherigen Ausführungen haben wir eine Reihe von Einschränkungen<br />
festgelegt (vgl. Abschnitt 2.5. Unsere Vereinfachungen reduzierten dabei die<br />
Anzahl der Unifikationsrelationen.<br />
63
Das im nächsten Abschnitt abgedruckte Programm wurde, wie oben gesagt,<br />
um einige Relationen erweitert. Für die Nutzerinteraktion kam nur der Dialog<br />
für die lokalen Variablen hinzu. Die <strong>and</strong>eren Relationen werden berechnet, wie<br />
bisher die Datentypen und Konstruktoren.<br />
Das Programm verlangt von den zu unifizierenden Modulen noch die Erfüllung<br />
der folgenden Bechränkungen:<br />
• Polymorphe Datentypen sind weiterhin ausgeschlossen.<br />
• Der Typ lokaler Variablen muss identisch sein; wir beziehen sie hier nicht<br />
in die Unifikation der Datentypdefinitionen ein, obwohl dies prinzipiell<br />
möglich ist.<br />
• Die Unifikation von Automatenzuständen geschieht nur auf der obersten<br />
Hierarchieebene; Subautomaten werden nur kopiert.<br />
• Höchstens einer von zwei unifzierten Zuständen darf Unterzustände besitzen.<br />
• Für die Ausdrücke von Ein-/Ausgaben, Vorbedingungen und Aktionen<br />
verlangen wird die Strukturäquivalenz der Terme für die Vereinigung.<br />
A.2 ODL Programm<br />
define setUnifiedMIFTerm(<br />
orig_one:MIFTerm, orig_two:MIFTerm, copy: MIFTerm,<br />
new_constructor_map:set(<br />
orig:(one:Constructor, two:Constructor), copy:Constructor),<br />
new_appl_map:set(orig:(one:Appl, two:Appl), copy:Appl),<br />
new_lvcn_map:set(<br />
orig:(one:LocVariableConst, two:LocVariableConst),<br />
copy:LocVariableConst)<br />
) as (<br />
exists ncon:new_constructor_map . (<br />
orig_one.Model = ncon.orig.one <strong>and</strong><br />
orig_two.Model = ncon.orig.two <strong>and</strong><br />
result has Model(copy, ncon.copy)<br />
) or<br />
exists napp:new_appl_map . (<br />
orig_one.Model = napp.orig.one <strong>and</strong><br />
orig_two.Model = napp.orig.two <strong>and</strong><br />
result has Model(copy, napp.copy)<br />
) or<br />
exists nlvar:new_lvcn_map . (<br />
orig_one.Model = nlvar.orig.one <strong>and</strong><br />
orig_two.Model = nlvar.orig.two <strong>and</strong><br />
result has Model(copy, nlvar.copy)<br />
) or<br />
( orig_one.Model = orig_two.Model <strong>and</strong><br />
result has Model(copy, orig_one.Model)<br />
)).<br />
64
define setCopiedMIFTerm(<br />
orig:MIFTerm, copy: MIFTerm,<br />
new_constructor_map:set(<br />
orig:(one:Constructor, two:Constructor), copy:Constructor),<br />
new_copy_constructor_map:set(<br />
orig:Constructor, copy:Constructor),<br />
new_appl_map:set(orig:(one:Appl, two:Appl), copy:Appl),<br />
new_copy_appl_map:set(orig:Appl, copy:Appl),<br />
new_lvcn_map:set(<br />
orig:(one:LocVariableConst, two:LocVariableConst),<br />
copy:LocVariableConst),<br />
new_copy_lvcn_map:set(<br />
orig:LocVariableConst, copy:LocVariableConst)<br />
) as (<br />
exists ncon:new_constructor_map . (<br />
( orig.Model = ncon.orig.one or orig.Model = ncon.orig.two<br />
) <strong>and</strong> result has Model(copy, ncon.copy)<br />
) or<br />
exists nccon:new_copy_constructor_map . (<br />
orig.Model = nccon.orig <strong>and</strong> result has Model(copy, nccon.copy)<br />
) or<br />
exists napp:new_appl_map . (<br />
( orig.Model = napp.orig.one or orig.Model = napp.orig.two<br />
) <strong>and</strong> result has Model(copy, napp.copy)<br />
) or<br />
exists ncapp:new_copy_appl_map . (<br />
orig.Model = ncapp.orig <strong>and</strong> result has Model(copy, ncapp.copy)<br />
) or<br />
exists nlvr:new_lvcn_map . (<br />
( orig.Model = nlvr.orig.one or orig.Model = nlvr.orig.two<br />
) <strong>and</strong> result has Model(copy, nlvr.copy)<br />
) or<br />
exists nclvr:new_copy_lvcn_map . (<br />
orig.Model=nclvr.orig <strong>and</strong> result has Model(copy,nclvr.copy)<br />
) or<br />
result has Model(copy, orig.Model)<br />
).<br />
define testSelectorType (<br />
sel:Selector, t:Type<br />
) := (<br />
exists ta:TAppl . (<br />
sel.Type = ta <strong>and</strong><br />
exists ta_arg:(ta.TArgs) . (<br />
indexOf(ta_arg, ta, "TArgs") = 1 <strong>and</strong><br />
ta_arg = t<br />
))).<br />
define selectorTypeEqual (<br />
65
sel1:Selector, sel2:Selector<br />
) := (<br />
sel1 = sel2 or<br />
/* search function applications */<br />
exists ta:{ta_test:(one:TAppl, two:TAppl) |<br />
ta_test.one = sel1.Type <strong>and</strong> ta_test.two = sel2.Type<br />
}<br />
exists ta_arg1:(ta.one.TArgs) . (<br />
indexOf(ta_arg1, ta.one, "TArgs") = 1 <strong>and</strong><br />
exists ta_arg2:(ta.two.TArgs) . (<br />
indexOf(ta_arg2, ta.two, "TArgs") = 1 <strong>and</strong><br />
/* compare result types */<br />
ta_arg1 = ta_arg2<br />
))).<br />
define testApplForConstructor(<br />
appl:(one:Appl, two:Appl),<br />
con:(one:Constructor, two:Constructor)<br />
) := (<br />
(appl.one.Head = con.one <strong>and</strong><br />
appl.two.Head = con.two<br />
) or<br />
(is Args(appl.one, con.one) <strong>and</strong><br />
is Args(appl.two, con.two) <strong>and</strong><br />
indexOf(con.one, appl.one, "Args") =<br />
indexOf(con.two, appl.two, "Args")<br />
)).<br />
define Consistent_Component_Port(<br />
component_pair:(one:Component, two:Component),<br />
pm_elem:(one:Port, two:Port)<br />
) := (<br />
pm_elem.one.Direction.IsEntry =<br />
pm_elem.two.Direction.IsEntry <strong>and</strong><br />
component_pair.one = pm_elem.one.Component <strong>and</strong><br />
component_pair.two = pm_elem.two.Component<br />
).<br />
define Consistent_Component_LocVar(<br />
component_pair:(one:Component, two:Component),<br />
lvm_elem:(one:LocVariable, two:LocVariable)<br />
) := (<br />
/* interne Abhängigkeit: Typgleichheit */<br />
lvm_elem.one.DefinedType.Model =<br />
lvm_elem.two.DefinedType.Model <strong>and</strong><br />
/* externe Abhängigkeit zu Component */<br />
component_pair.one = lvm_elem.one.Component <strong>and</strong><br />
component_pair.two = lvm_elem.two.Component<br />
).<br />
66
define Consistent_State_Transition(<br />
state_map:set (one:State, two:State),<br />
tm_one:TransitionSegment,<br />
tm_two:TransitionSegment<br />
) := (<br />
exists sm_elem_source:state_map . (<br />
tm_one.SourcePoint.State = sm_elem_source.one <strong>and</strong><br />
tm_two.SourcePoint.State = sm_elem_source.two<br />
) <strong>and</strong><br />
exists sm_elem_destin:state_map . (<br />
tm_one.DestinationPoint.State = sm_elem_destin.one <strong>and</strong><br />
tm_two.DestinationPoint.State = sm_elem_destin.two<br />
)).<br />
define Consistent_Port_TypeDef(<br />
port_map:set(one:Port, two:Port),<br />
tdm_elem:(one:DataDef, two:DataDef)<br />
) := (<br />
exists pm_elem:port_map . (<br />
tdm_elem.one.TConst = pm_elem.one.DefinedType.Model <strong>and</strong><br />
tdm_elem.two.TConst = pm_elem.two.DefinedType.Model<br />
)).<br />
define Consistent_Const_TypeDef(<br />
con_map:set(one:Constructor, two:Constructor),<br />
tdm_elem:(one:DataDef, two:DataDef)<br />
) := (<br />
exists cm_elem:con_map . (<br />
tdm_elem.one = cm_elem.one.DataDef <strong>and</strong><br />
tdm_elem.two = cm_elem.two.DataDef<br />
)).<br />
/* Component */<br />
context component_pair:(one:Component, two:Component) .<br />
/* Port */<br />
exists port_map_possible:<br />
lfp FPPRTMP set fpprtmp_it:(<br />
one:(component_pair.one.Ports),<br />
two:(component_pair.two.Ports)<br />
) with (<br />
call Consistent_Component_Port(component_pair, fpprtmp_it)<br />
) .<br />
context port_map:{pm_test:set port_map_possible |<br />
forall pm1:pm_test . forall pm2:pm_test . (<br />
(pm1.one = pm2.one) equiv (pm1.two = pm2.two)<br />
)<br />
}.<br />
67
* LocVariable */<br />
exists lvar_map_possible:<br />
lfp FPLVRMP set fplvrmp_it:(<br />
one:(component_pair.one.LocVariables),<br />
two:(component_pair.two.LocVariables)<br />
) with (<br />
call Consistent_Component_LocVar(component_pair, fplvrmp_it)<br />
).<br />
context lvar_map:{lvm_test:set lvar_map_possible |<br />
forall lvm1:lvm_test . forall lvm2:lvm_test . (<br />
(lvm1.one = lvm2.one) equiv (lvm1.two = lvm2.two)<br />
)<br />
}.<br />
/* LocVariableConst */<br />
exists lvarconst_map:<br />
lfp FPLVCM set fplvcm_it:(<br />
one:LocVariableConst,<br />
two:LocVariableConst<br />
) with (<br />
exists lvm_fplvcm:lvar_map . (<br />
fplvcm_it.one.LocVariable = lvm_fplvcm.one <strong>and</strong><br />
fplvcm_it.two.LocVariable = lvm_fplvcm.two<br />
)<br />
).<br />
/* State */<br />
context state_map:{sm_test:set (<br />
one:(component_pair.one.Automaton.State.SubStates),<br />
two:(component_pair.two.Automaton.State.SubStates)) |<br />
/* at least one state must be unified, */<br />
/* otherwise the automaton would not be connected */<br />
exists sm0:sm_test . true <strong>and</strong><br />
forall sm1:sm_test . (<br />
forall sm2:sm_test . (<br />
(sm1.one = sm2.one) equiv (sm1.two = sm2.two)<br />
) <strong>and</strong> (<br />
/*at most one state may have a substates */<br />
neg exists sm_one_sub:(sm1.one.SubStates) . true or<br />
neg exists sm_two_sub:(sm1.two.SubStates) . true<br />
))<br />
}.<br />
/* TransitionSegment */<br />
exists transition_map_possible:<br />
lfp FPTSGMP set fptsgmp_it:(<br />
one:(component_pair.one.Automaton.State.TransitionSegments),<br />
two:(component_pair.two.Automaton.State.TransitionSegments)<br />
) with (<br />
call Consistent_State_Transition(state_map,<br />
68
fptsgmp_it.one, fptsgmp_it.two)<br />
) .<br />
context transition_map:{tm_test:set transition_map_possible |<br />
forall tm1:tm_test . forall tm2:tm_test . (<br />
(tm1.one = tm2.one) equiv (tm1.two = tm2.two)<br />
)<br />
}.<br />
/* Input */<br />
exists input_map: lfp FPINPM set fpinpm_it:(one:Input, two:Input)<br />
with (<br />
exists tm_fpinpm:transition_map . (<br />
tm_fpinpm.one = fpinpm_it.one.TransitionSegment <strong>and</strong><br />
tm_fpinpm.two = fpinpm_it.two.TransitionSegment<br />
) <strong>and</strong><br />
exists pm_fpinpm:port_map . (<br />
fpinpm_it.one.Port.Model = pm_fpinpm.one <strong>and</strong><br />
fpinpm_it.two.Port.Model = pm_fpinpm.two<br />
)<br />
).<br />
/* Output */<br />
exists output_map: lfp FPO set fpoutm_it:(one:Output, two:Output)<br />
with (<br />
exists tm_fpoutm:transition_map . (<br />
tm_fpoutm.one = fpoutm_it.one.TransitionSegment <strong>and</strong><br />
tm_fpoutm.two = fpoutm_it.two.TransitionSegment<br />
) <strong>and</strong><br />
exists pm_fpoutm:port_map . (<br />
fpoutm_it.one.Port.Model = pm_fpoutm.one <strong>and</strong><br />
fpoutm_it.two.Port.Model = pm_fpoutm.two<br />
)).<br />
/* Condition */<br />
exists condition_map: lfp FPCNDM set<br />
fpcndm_it:(one:Condition, two:Condition)<br />
with (<br />
exists tm_fpcndm:transition_map . (<br />
tm_fpcndm.one = fpcndm_it.one.TransitionSegment <strong>and</strong><br />
tm_fpcndm.two = fpcndm_it.two.TransitionSegment<br />
)).<br />
/* Action */<br />
exists action_map: lfp FPACTM set<br />
fpactm_it:(one:Action, two:Action)<br />
with (<br />
exists tm_fpactm:transition_map . (<br />
tm_fpactm.one = fpactm_it.one.TransitionSegment <strong>and</strong><br />
tm_fpactm.two = fpactm_it.two.TransitionSegment<br />
) <strong>and</strong><br />
69
exists lvm_fpactm:lvar_map . (<br />
fpactm_it.one.LocVariable.Model = lvm_fpactm.one <strong>and</strong><br />
fpactm_it.two.LocVariable.Model = lvm_fpactm.two<br />
)).<br />
/* InterfacePoint */<br />
exists interfacepoint_map: lfp FPIFPM set<br />
fpifpm_it:(one:InterfacePoint, two:InterfacePoint)<br />
with (<br />
exists tm_fpifpm:transition_map . (<br />
(tm_fpifpm.one.SourcePoint = fpifpm_it.one <strong>and</strong><br />
tm_fpifpm.two.SourcePoint = fpifpm_it.two<br />
) or<br />
(tm_fpifpm.one.DestinationPoint = fpifpm_it.one <strong>and</strong><br />
tm_fpifpm.two.DestinationPoint = fpifpm_it.two<br />
))).<br />
/* Appl */<br />
exists appl_map: lfp FPAPPM set fpappm_it:(one:Appl, two:Appl)<br />
with (<br />
exists im_fpappm:input_map . (<br />
im_fpappm.one.Pattern.Model = fpappm_it.one <strong>and</strong><br />
im_fpappm.two.Pattern.Model = fpappm_it.two<br />
) or<br />
exists om_fpappm:output_map . (<br />
om_fpappm.one.Expression.Model = fpappm_it.one <strong>and</strong><br />
om_fpappm.two.Expression.Model = fpappm_it.two<br />
) or<br />
exists cndm_fpappm:condition_map . (<br />
cndm_fpappm.one.Expression.Model = fpappm_it.one <strong>and</strong><br />
cndm_fpappm.two.Expression.Model = fpappm_it.two<br />
) or<br />
exists actm_fpappm:action_map . (<br />
actm_fpappm.one.Value.Model = fpappm_it.one <strong>and</strong><br />
actm_fpappm.two.Value.Model = fpappm_it.two<br />
) or<br />
exists fpappm_elem:FPAPPM . (<br />
is Args(fpappm_elem.one, fpappm_it.one) <strong>and</strong><br />
is Args(fpappm_elem.two, fpappm_it.two) <strong>and</strong><br />
indexOf(fpappm_it.one, fpappm_elem.one, "Args") =<br />
indexOf(fpappm_it.two, fpappm_elem.two, "Args")<br />
)).<br />
/* Constructor */<br />
exists constructor_map: lfp FPCONM set<br />
fpconm_it:(one:Constructor, two:Constructor)<br />
with (<br />
exists im_fpconm:input_map . (<br />
fpconm_it.one = im_fpconm.one.Pattern.Model <strong>and</strong><br />
fpconm_it.two = im_fpconm.two.Pattern.Model<br />
70
) or<br />
exists om_fpconm:output_map . (<br />
fpconm_it.one = om_fpconm.one.Expression.Model <strong>and</strong><br />
fpconm_it.two = om_fpconm.two.Expression.Model<br />
) or<br />
exists am_fpconm:appl_map .<br />
call testApplForConstructor(am_fpconm, fpconm_it)<br />
).<br />
/* DataDef */<br />
exists typedef_map:lfp FPTDF set<br />
fp_tdef_it:(one:DataDef, two:DataDef)<br />
with (<br />
call Consistent_Port_TypeDef(port_map, fp_tdef_it) or<br />
call Consistent_Const_TypeDef(constructor_map, fp_tdef_it)<br />
).<br />
/* Selector */<br />
exists selector_map: lfp FPSELM set<br />
fpselm_it:(one:Selector, two:Selector)<br />
with (<br />
exists cm_fpselm:constructor_map . (<br />
fpselm_it.one.Constructor = cm_fpselm.one <strong>and</strong><br />
fpselm_it.two.Constructor = cm_fpselm.two <strong>and</strong><br />
indexOf(fpselm_it.one, cm_fpselm.one, "Selectors") =<br />
indexOf(fpselm_it.two, cm_fpselm.two, "Selectors")<br />
)).<br />
/* START OF consistency tests on generated mappings */<br />
/* checks consistency of appl unification relation against<br />
itself, constructor, locvariable unification relation or<br />
subexpression equality.<br />
*/<br />
define checkApplHeadArgsConsistency(<br />
a_map:set (one:Appl, two:Appl),<br />
c_map:set (one:Constructor, two:Constructor),<br />
l_map:set (one:LocVariableConst, two:LocVariableConst)<br />
) := (<br />
forall ae:a_map . (<br />
/* check function application */<br />
(<br />
/* applied function is equal */<br />
ae.one.Head = ae.two.Head<br />
or<br />
/* constructor */<br />
exists ce1:c_map . (<br />
ae.one.Head = ce1.one <strong>and</strong> ae.two.Head = ce1.two<br />
)<br />
) <strong>and</strong><br />
71
forall ap1:(ae.one.Args) . exists ap2:(ae.two.Args) . (<br />
indexOf(ap1,ae.one,"Args") = indexOf(ap2,ae.two,"Args") <strong>and</strong><br />
(<br />
/* unified Constructor */<br />
exists ce2:c_map . (ap1 = ce2.one <strong>and</strong> ap2 = ce2.two) or<br />
/* unified Appl */<br />
exists ae2:a_map . (ap1 = ae2.one <strong>and</strong> ap2 = ae2.two) or<br />
/* unified LVar */<br />
exists le2:l_map . (ap1 = le2.one <strong>and</strong> ap2 = le2.two) or<br />
/* some other equal Const */<br />
ap1 = ap2<br />
)))).<br />
/* checks consistency of input unification relation against<br />
appl, constructor, locvariable unification relation.<br />
*/<br />
define checkInputPatternConsistency(<br />
i_map:set (one:Input, two:Input),<br />
a_map:set (one:Appl, two:Appl),<br />
c_map:set (one:Constructor, two:Constructor),<br />
l_map:set (one:LocVariableConst, two:LocVariableConst)<br />
) := (<br />
forall ie:i_map . (<br />
exists ce:c_map . (<br />
ie.one.Pattern.Model=ce.one <strong>and</strong> ie.two.Pattern.Model=ce.two<br />
) or<br />
exists ae:a_map . (<br />
ie.one.Pattern.Model=ae.one <strong>and</strong> ie.two.Pattern.Model=ae.two<br />
) or<br />
ie.one.Pattern.Model=ie.two.Pattern.Model<br />
)).<br />
/* checks consistency of output unification relation against<br />
appl, constructor, locvariable unification relation.<br />
*/<br />
define checkOutputExpressionConsistency(<br />
o_map:set (one:Output, two:Output),<br />
a_map:set (one:Appl, two:Appl),<br />
c_map:set (one:Constructor, two:Constructor),<br />
l_map:set (one:LocVariableConst, two:LocVariableConst)<br />
) := (<br />
forall oe:o_map . (<br />
exists ce:c_map . (<br />
oe.one.Expression.Model = ce.one <strong>and</strong><br />
oe.two.Expression.Model = ce.two<br />
) or<br />
exists ae:a_map . (<br />
oe.one.Expression.Model = ae.one <strong>and</strong><br />
oe.two.Expression.Model = ae.two<br />
) or<br />
72
oe.one.Expression.Model = oe.two.Expression.Model<br />
)).<br />
(<br />
/* check appl for consistency */<br />
call checkApplHeadArgsConsistency(appl_map,<br />
constructor_map, lvarconst_map) <strong>and</strong><br />
/* check inputs for consistency */<br />
call checkInputPatternConsistency(input_map, appl_map,<br />
constructor_map, lvarconst_map) <strong>and</strong><br />
/* check outputs for consistency */<br />
call checkOutputExpressionConsistency(output_map, appl_map,<br />
constructor_map, lvarconst_map) <strong>and</strong><br />
/* uniqueness test for datadef */<br />
forall tdm1tst:typedef_map . forall tdm2tst:typedef_map . (<br />
tdm1tst.one = tdm2tst.one equiv tdm1tst.two = tdm2tst.two<br />
) <strong>and</strong><br />
/* uniqueness test for interfacepoint */<br />
forall ifpm1tst:interfacepoint_map .<br />
forall ifpm2tst:interfacepoint_map . (<br />
ifpm1tst.one = ifpm2tst.one equiv ifpm1tst.two = ifpm2tst.two<br />
) <strong>and</strong><br />
/* uniqueness test for constructor */<br />
forall cm1tst:constructor_map .<br />
forall cm2tst:constructor_map . (<br />
cm1tst.one = cm2tst.one equiv cm1tst.two = cm2tst.two<br />
) <strong>and</strong><br />
/* Port Copy */<br />
exists copy_port_set: lfp FP_CPS set fpcps_it:Port<br />
with (<br />
neg exists pm_fpcps:port_map . (<br />
fpcps_it = pm_fpcps.one or fpcps_it = pm_fpcps.two<br />
) <strong>and</strong> (<br />
fpcps_it.Component = component_pair.one or<br />
fpcps_it.Component = component_pair.two<br />
)).<br />
/* LocVariable Copy */<br />
exists copy_lvar_set: lfp FP_CLVRS set fpclvrs_it:LocVariable<br />
with (<br />
neg exists lvrm_fpclvrs:lvar_map . (<br />
fpclvrs_it = lvrm_fpclvrs.one or fpclvrs_it = lvrm_fpclvrs.two<br />
) <strong>and</strong> (<br />
fpclvrs_it.Component = component_pair.one or<br />
73
fpclvrs_it.Component = component_pair.two<br />
)).<br />
/* LocVariableConst Copy */<br />
exists copy_lvarconst_set: lfp FPCLVCS set<br />
fpclvcs_it: LocVariableConst<br />
with (<br />
neg exists lvcm_fpclvcs:lvarconst_map . (<br />
fpclvcs_it = lvcm_fpclvcs.one or<br />
fpclvcs_it = lvcm_fpclvcs.two<br />
) <strong>and</strong> (<br />
fpclvcs_it.LocVariable.Component = component_pair.one or<br />
fpclvcs_it.LocVariable.Component = component_pair.two<br />
)).<br />
/* State Copy */<br />
exists copy_state_set: lfp FP_CSS set fpcss_it:State<br />
with (<br />
neg exists sm_fpcss:state_map . (<br />
fpcss_it = sm_fpcss.one or fpcss_it = sm_fpcss.two<br />
) <strong>and</strong> (<br />
(fpcss_it.SuperState = component_pair.one.Automaton.State or<br />
fpcss_it.SuperState = component_pair.two.Automaton.State<br />
) or<br />
exists css_fpcss:FP_CSS . (<br />
fpcss_it.SuperState = css_fpcss<br />
) or<br />
exists sm2_fpcss:state_map . (<br />
fpcss_it.SuperState = sm2_fpcss.one or<br />
fpcss_it.SuperState = sm2_fpcss.two<br />
))).<br />
/* TransitionSegment Copy */<br />
exists copy_transition_set: lfp FP_CTS set<br />
fpcts_it:TransitionSegment<br />
with (<br />
neg exists tm_fpcts:transition_map . (<br />
fpcts_it = tm_fpcts.one or fpcts_it = tm_fpcts.two<br />
) <strong>and</strong> (<br />
(fpcts_it.State = component_pair.one.Automaton.State or<br />
fpcts_it.State = component_pair.two.Automaton.State<br />
) or<br />
exists css_fpcts:copy_state_set . (<br />
fpcts_it.State = css_fpcts<br />
) or<br />
exists sm_fpcts:state_map . (<br />
fpcts_it.State = sm_fpcts.one or<br />
fpcts_it.State = sm_fpcts.two<br />
))).<br />
74
* Input Copy */<br />
exists copy_input_set: lfp FP_CIS set fpcis_it:Input<br />
with (<br />
neg exists im_fpcis:input_map . (<br />
fpcis_it = im_fpcis.one or<br />
fpcis_it = im_fpcis.two<br />
) <strong>and</strong> (<br />
exists cts_fpcis:copy_transition_set . (<br />
fpcis_it.TransitionSegment = cts_fpcis<br />
) or<br />
exists tm_fpcis:transition_map . (<br />
fpcis_it.TransitionSegment = tm_fpcis.one or<br />
fpcis_it.TransitionSegment = tm_fpcis.two<br />
))).<br />
/* Output Copy */<br />
exists copy_output_set: lfp FP_COS set fpcos_it:Output<br />
with (<br />
neg exists om_fpcos:output_map . (<br />
om_fpcos.one = fpcos_it or<br />
om_fpcos.two = fpcos_it<br />
) <strong>and</strong> (<br />
exists cts_fpcos:copy_transition_set . (<br />
fpcos_it.TransitionSegment = cts_fpcos<br />
) or<br />
exists tm_fpcos:transition_map . (<br />
fpcos_it.TransitionSegment = tm_fpcos.one or<br />
fpcos_it.TransitionSegment = tm_fpcos.two<br />
))).<br />
/* Condtion Copy */<br />
exists copy_condition_set: lfp FP_CCNDS set fpccnds_it:Condition<br />
with (<br />
neg exists cndm_fpccnds:condition_map . (<br />
fpccnds_it = cndm_fpccnds.one or<br />
fpccnds_it = cndm_fpccnds.two<br />
) <strong>and</strong><br />
(<br />
exists cts_fpccnds:copy_transition_set . (<br />
fpccnds_it.TransitionSegment = cts_fpccnds<br />
) or<br />
exists tm_fpccnds:transition_map . (<br />
fpccnds_it.TransitionSegment = tm_fpccnds.one or<br />
fpccnds_it.TransitionSegment = tm_fpccnds.two<br />
)<br />
)<br />
) .<br />
/* Action Copy */<br />
75
exists copy_action_set: lfp FP_CACTS set fpcacts_it:Action<br />
with (<br />
neg exists actm_fpcacts:action_map . (<br />
fpcacts_it = actm_fpcacts.one or<br />
fpcacts_it = actm_fpcacts.two<br />
) <strong>and</strong><br />
(<br />
exists cts_fpcacts:copy_transition_set . (<br />
fpcacts_it.TransitionSegment = cts_fpcacts<br />
) or<br />
exists tm_fpcacts:transition_map . (<br />
fpcacts_it.TransitionSegment = tm_fpcacts.one or<br />
fpcacts_it.TransitionSegment = tm_fpcacts.two<br />
)<br />
)<br />
) .<br />
/* InterfacePoint Copy */<br />
exists copy_interfacepoint_set: lfp FP_CIFPS set<br />
fpcifps_it:InterfacePoint<br />
with (<br />
neg exists ifpm_fpcifps:interfacepoint_map . (<br />
fpcifps_it = ifpm_fpcifps.one or<br />
fpcifps_it = ifpm_fpcifps.two<br />
) <strong>and</strong> (<br />
(fpcifps_it.State = component_pair.one.Automaton.State or<br />
fpcifps_it.State = component_pair.two.Automaton.State<br />
) or<br />
exists css_fpcifps:copy_state_set . (<br />
fpcifps_it.State = css_fpcifps<br />
) or<br />
exists sm_fpcifps:state_map . (<br />
fpcifps_it.State = sm_fpcifps.one or<br />
fpcifps_it.State = sm_fpcifps.two<br />
))).<br />
/* Appl Copy */<br />
exists copy_appl_set: lfp FPCAPPS set fpcapps_it:Appl<br />
with (<br />
neg exists appm_fpcapps:appl_map . (<br />
fpcapps_it = appm_fpcapps.one or<br />
fpcapps_it = appm_fpcapps.two<br />
) <strong>and</strong> (<br />
exists cis_fpcapps:copy_input_set . (<br />
fpcapps_it = cis_fpcapps.Pattern.Model<br />
) or<br />
exists cos_fpcapps:copy_output_set . (<br />
fpcapps_it = cos_fpcapps.Expression.Model<br />
) or<br />
exists ccnds_fpcapps:copy_condition_set . (<br />
76
fpcapps_it = ccnds_fpcapps.Expression.Model<br />
) or<br />
exists cacts_fpcapps:copy_action_set . (<br />
fpcapps_it = cacts_fpcapps.Value.Model<br />
) or<br />
exists im_fpcapps:input_map . (<br />
im_fpcapps.one.Pattern.Model = fpcapps_it or<br />
im_fpcapps.two.Pattern.Model = fpcapps_it<br />
) or<br />
exists om_fpcapps:output_map . (<br />
om_fpcapps.one.Expression.Model = fpcapps_it or<br />
om_fpcapps.two.Expression.Model = fpcapps_it<br />
) or<br />
exists cndm_fpcapps:condition_map . (<br />
cndm_fpcapps.one.Expression.Model = fpcapps_it or<br />
cndm_fpcapps.two.Expression.Model = fpcapps_it<br />
) or<br />
exists actm_fpcapps:action_map . (<br />
actm_fpcapps.one.Value.Model = fpcapps_it or<br />
actm_fpcapps.two.Value.Model = fpcapps_it<br />
) or<br />
exists fpcapps_elem:FPCAPPS . (<br />
is Args(fpcapps_elem, fpcapps_it)<br />
))).<br />
/* Constructor Copy */<br />
exists copy_constructor_set: lfp FPCCS set fpccs_it:Constructor<br />
with (<br />
neg exists cm_fpccs:constructor_map . (<br />
cm_fpccs.one = fpccs_it or cm_fpccs.two = fpccs_it<br />
) <strong>and</strong><br />
exists tm_fpccs:typedef_map . (<br />
fpccs_it.DataDef = tm_fpccs.one or<br />
fpccs_it.DataDef = tm_fpccs.two<br />
)).<br />
/* Selector Copy */<br />
exists copy_selector_set: lfp FPCSS set fpcss_it:Selector<br />
with (<br />
exists ccm_fpcss:copy_constructor_set . (<br />
fpcss_it.Constructor = ccm_fpcss<br />
)<br />
) .<br />
/* new component name */<br />
context new_comp_name:String .<br />
/* START of building */<br />
exists new_dtd_module: new DTDModule .<br />
77
exists new_automaton:new Automaton .<br />
exists new_root_state: new State .<br />
exists new_comp: new Component . (<br />
result has Name(new_comp, new_comp_name) <strong>and</strong><br />
result has SubComponents(component_pair.one.SuperComponent,<br />
new_comp) <strong>and</strong><br />
result has Name(new_dtd_module,<br />
concat("created_dtd_module_", new_comp_name)) <strong>and</strong><br />
result has Name(new_automaton, "Automaton") <strong>and</strong><br />
result has Name(new_root_state, "rootState") <strong>and</strong><br />
result has DTDModules(component_pair.one.SuperComponent.DTD,<br />
new_dtd_module) <strong>and</strong><br />
result has Automaton(new_comp, new_automaton) <strong>and</strong><br />
result has State(new_automaton, new_root_state) <strong>and</strong><br />
/* build datadefs */<br />
exists new_typedef_map:map orig:typedef_map<br />
to copy:new DataDef . (<br />
forall ntm1:new_typedef_map . forall ntm2:new_typedef_map . (<br />
ntm1.orig = ntm2.orig equiv ntm1.copy = ntm2.copy<br />
) <strong>and</strong><br />
forall mk_tdef:new_typedef_map . (<br />
result has TypeDefs(new_dtd_module, mk_tdef.copy) <strong>and</strong><br />
result has Name(mk_tdef.copy,<br />
concat(mk_tdef.orig.one.Name, mk_tdef.orig.two.Name))<br />
) <strong>and</strong><br />
/* build corresponding abstract_types */<br />
exists new_tconst_map:map orig:typedef_map<br />
to copy:new AbstractType . (<br />
forall ntm3:new_tconst_map . forall ntm4:new_tconst_map . (<br />
ntm3.orig = ntm4.orig equiv ntm3.copy = ntm4.copy<br />
) <strong>and</strong><br />
forall mk_tcon:new_tconst_map . (<br />
result has TConsts(new_dtd_module, mk_tcon.copy) <strong>and</strong><br />
result has Name(mk_tcon.copy,<br />
concat(mk_tcon.orig.one.Name,mk_tcon.orig.two.Name)) <strong>and</strong><br />
exists ntm_elem_tcon:new_typedef_map . (<br />
mk_tcon.orig = ntm_elem_tcon.orig <strong>and</strong><br />
result has TConst(ntm_elem_tcon.copy, mk_tcon.copy)<br />
)) <strong>and</strong><br />
/* build ports */<br />
exists new_port_map:map orig:port_map to copy:new Port . (<br />
forall npm1:new_port_map . forall npm2:new_port_map . (<br />
npm1.orig = npm2.orig equiv npm1.copy = npm2.copy<br />
) <strong>and</strong><br />
forall mk_prt:new_port_map . (<br />
result has Ports(new_comp, mk_prt.copy) <strong>and</strong><br />
result has Name(mk_prt.copy,<br />
78
concat(mk_prt.orig.one.Name, mk_prt.orig.two.Name)) <strong>and</strong><br />
exists mk_prt_nd:new Direction . (<br />
result has Direction(mk_prt.copy, mk_prt_nd) <strong>and</strong><br />
result has IsEntry(mk_prt_nd,<br />
mk_prt.orig.one.Direction.IsEntry)<br />
) <strong>and</strong><br />
exists mk_prt_mt:new MIFType . (<br />
result has DefinedType(mk_prt.copy, mk_prt_mt) <strong>and</strong><br />
exists ntm_elem_port:new_tconst_map . (<br />
mk_prt.orig.one.DefinedType.Model =<br />
ntm_elem_port.orig.one.TConst <strong>and</strong><br />
mk_prt.orig.two.DefinedType.Model =<br />
ntm_elem_port.orig.two.TConst <strong>and</strong><br />
result has Model(mk_prt_mt, ntm_elem_port.copy)<br />
))) <strong>and</strong><br />
/* copy ports */<br />
exists new_copy_port_map:map orig:copy_port_set<br />
to copy:new Port . (<br />
forall ncpm1:new_copy_port_map .<br />
forall ncpm2:new_copy_port_map . (<br />
ncpm1.orig = ncpm2.orig equiv ncpm1.copy = ncpm2.copy<br />
) <strong>and</strong><br />
forall cp_prt:new_copy_port_map . (<br />
result has Ports(new_comp, cp_prt.copy) <strong>and</strong><br />
result has Name(cp_prt.copy, cp_prt.orig.Name) <strong>and</strong><br />
exists cp_prt_nd:new Direction . (<br />
result has Direction(cp_prt.copy, cp_prt_nd) <strong>and</strong><br />
result has IsEntry(cp_prt_nd, cp_prt.orig.Direction.IsEntry)<br />
) <strong>and</strong><br />
exists cp_prt_mt:new MIFType . (<br />
result has DefinedType(cp_prt.copy, cp_prt_mt) <strong>and</strong><br />
result has Model(cp_prt_mt, cp_prt.orig.DefinedType.Model)<br />
)) <strong>and</strong><br />
/* build constructors */<br />
exists new_constructor_map:map orig:constructor_map<br />
to copy:new Constructor . (<br />
forall ncm1:new_constructor_map .<br />
forall ncm2:new_constructor_map . (<br />
ncm1.orig = ncm2.orig equiv ncm1.copy = ncm2.copy<br />
) <strong>and</strong><br />
forall mk_cons:new_constructor_map . (<br />
result has Name(mk_cons.copy,<br />
concat(mk_cons.orig.one.Name,mk_cons.orig.two.Name)) <strong>and</strong><br />
result has Consts(new_dtd_module, mk_cons.copy) <strong>and</strong><br />
exists ntm_elem_cons:new_typedef_map . (<br />
mk_cons.orig.one.DataDef = ntm_elem_cons.orig.one <strong>and</strong><br />
mk_cons.orig.two.DataDef = ntm_elem_cons.orig.two <strong>and</strong><br />
result has Constructors(ntm_elem_cons.copy, mk_cons.copy)<br />
79
)) <strong>and</strong><br />
/* copy constructors not merged */<br />
exists new_copy_constructor_map:map orig:copy_constructor_set<br />
to copy:new Constructor . (<br />
forall nccm1:new_copy_constructor_map .<br />
forall nccm2:new_copy_constructor_map . (<br />
nccm1.orig = nccm2.orig equiv nccm1.copy = nccm2.copy<br />
) <strong>and</strong><br />
forall cp_cons:new_copy_constructor_map . (<br />
result has Name(cp_cons.copy, cp_cons.orig.Name) <strong>and</strong><br />
result has Consts(new_dtd_module, cp_cons.copy) <strong>and</strong><br />
exists ntm_elem_ccons:new_typedef_map . (<br />
(cp_cons.orig.DataDef = ntm_elem_ccons.orig.one or<br />
cp_cons.orig.DataDef = ntm_elem_ccons.orig.two<br />
) <strong>and</strong><br />
result has Constructors(ntm_elem_ccons.copy, cp_cons.copy)<br />
)) <strong>and</strong><br />
/* build selectors */<br />
exists new_selector_map:map orig:selector_map<br />
to copy:new Selector . (<br />
forall nselm1:new_selector_map .<br />
forall nselm2:new_selector_map . (<br />
nselm1.orig = nselm2.orig equiv nselm1.copy = nselm2.copy<br />
) <strong>and</strong><br />
forall mk_sels:new_selector_map . (<br />
result has Name(mk_sels.copy,<br />
concat(mk_sels.orig.one.Name,mk_sels.orig.two.Name)) <strong>and</strong><br />
result has Consts(new_dtd_module, mk_sels.copy) <strong>and</strong><br />
exists ncm_elem_sels:new_constructor_map . (<br />
mk_sels.orig.one.Constructor = ncm_elem_sels.orig.one <strong>and</strong><br />
mk_sels.orig.two.Constructor = ncm_elem_sels.orig.two <strong>and</strong><br />
result has Selectors(ncm_elem_sels.copy, mk_sels.copy,<br />
indexOf(mk_sels.orig.one,<br />
mk_sels.orig.one.Constructor, "Selectors"))<br />
) <strong>and</strong><br />
exists ntappl1:new TAppl . (<br />
exists tcnst1:TConst . (<br />
tcnst1.Name = "->" <strong>and</strong><br />
result has THead(ntappl1, tcnst1)<br />
) <strong>and</strong><br />
exists ntcm1_elem_sels:new_tconst_map . (<br />
mk_sels.orig.one.Constructor.DataDef =<br />
ntcm1_elem_sels.orig.one <strong>and</strong><br />
mk_sels.orig.two.Constructor.DataDef =<br />
ntcm1_elem_sels.orig.two <strong>and</strong><br />
result has TArgs(ntappl1, ntcm1_elem_sels.copy, 0)<br />
) <strong>and</strong><br />
exists ntcm2_elem_sels:new_tconst_map . (<br />
80
call testSelectorType(mk_sels.orig.one,<br />
ntcm2_elem_sels.orig.one.TConst) <strong>and</strong><br />
call testSelectorType(mk_sels.orig.two,<br />
ntcm2_elem_sels.orig.two.TConst) <strong>and</strong><br />
result has TArgs(ntappl1, ntcm2_elem_sels.copy, 1)<br />
)<br />
<strong>and</strong> result has DefinedType(mk_sels.copy, ntappl1)<br />
)) <strong>and</strong><br />
/* copy selectors */<br />
exists new_copy_selector_map: map orig:copy_selector_set<br />
to copy:new Selector . (<br />
forall cselm1:new_copy_selector_map .<br />
forall cselm2:new_copy_selector_map . (<br />
cselm1.orig = cselm2.orig equiv cselm1.copy = cselm2.copy<br />
) <strong>and</strong><br />
forall cp_sels:new_copy_selector_map . (<br />
result has Name(cp_sels.copy, cp_sels.orig.Name) <strong>and</strong><br />
result has Consts(new_dtd_module, cp_sels.copy) <strong>and</strong><br />
exists nccm_elem_sels:new_copy_constructor_map . (<br />
cp_sels.orig.Constructor = nccm_elem_sels.orig <strong>and</strong><br />
result has Selectors(nccm_elem_sels.copy, cp_sels.copy,<br />
indexOf(cp_sels.orig,<br />
cp_sels.orig.Constructor, "Selectors"))<br />
) <strong>and</strong><br />
exists ntappl2:new TAppl . (<br />
exists tcnst2:TConst . (<br />
tcnst2.Name = "->" <strong>and</strong><br />
result has THead(ntappl2, tcnst2)<br />
) <strong>and</strong><br />
exists ntcm3_elem_sels:new_tconst_map . (<br />
(cp_sels.orig.Constructor.DataDef =<br />
ntcm3_elem_sels.orig.one or<br />
cp_sels.orig.Constructor.DataDef =<br />
ntcm3_elem_sels.orig.two<br />
) <strong>and</strong><br />
result has TArgs(ntappl2, ntcm3_elem_sels.copy, 0)<br />
) <strong>and</strong><br />
exists tcnst3:TConst . (<br />
call testSelectorType(cp_sels.orig, tcnst3) <strong>and</strong><br />
result has TArgs(ntappl2, tcnst3, 1)<br />
) <strong>and</strong><br />
result has DefinedType(cp_sels.copy, ntappl2)<br />
)) <strong>and</strong><br />
/* build states */<br />
exists new_state_map:map orig:state_map to copy:new State . (<br />
forall nsm1:new_state_map . forall nsm2:new_state_map . (<br />
nsm1.orig = nsm2.orig equiv nsm1.copy = nsm2.copy<br />
) <strong>and</strong><br />
81
* merged states are part of root state only */<br />
forall mk_stt:new_state_map . (<br />
result has Name(mk_stt.copy,<br />
concat(mk_stt.orig.one.Name, mk_stt.orig.two.Name)) <strong>and</strong><br />
result has SubStates(new_root_state, mk_stt.copy)<br />
) <strong>and</strong><br />
/* copy states not merged */<br />
exists new_copy_state_map:map orig:copy_state_set<br />
to copy:new State . (<br />
forall ncsm1:new_copy_state_map .<br />
forall ncsm2:new_copy_state_map . (<br />
ncsm1.orig = ncsm2.orig equiv ncsm1.copy = ncsm2.copy<br />
) <strong>and</strong><br />
forall cp_stt:new_copy_state_map . (<br />
result has Name(cp_stt.copy, cp_stt.orig.Name) <strong>and</strong> (<br />
exists nsm_elem_cpstt:new_state_map . (<br />
(is SubStates(nsm_elem_cpstt.orig.one, cp_stt.orig) or<br />
is SubStates(nsm_elem_cpstt.orig.two, cp_stt.orig)<br />
) <strong>and</strong><br />
result has SubStates(nsm_elem_cpstt.copy, cp_stt.copy)<br />
) or<br />
exists ncsm_elem_cpstt:new_copy_state_map . (<br />
is SubStates(ncsm_elem_cpstt.orig, cp_stt.orig) <strong>and</strong><br />
result has SubStates(ncsm_elem_cpstt.copy, cp_stt.copy)<br />
) or<br />
result has SubStates(new_root_state, cp_stt.copy)<br />
)) <strong>and</strong><br />
/* build transitionsegments */<br />
exists new_transition_map:map orig:transition_map<br />
to copy:new TransitionSegment . (<br />
forall ntm5:new_transition_map .<br />
forall ntm6:new_transition_map . (<br />
ntm5.orig = ntm6.orig equiv ntm5.copy = ntm6.copy<br />
) <strong>and</strong><br />
forall mk_tsg:new_transition_map . (<br />
result has TransitionSegments(new_root_state, mk_tsg.copy)<br />
) <strong>and</strong><br />
/* copy transitionssegments */<br />
exists new_copy_transition_map:map orig:copy_transition_set<br />
to copy:new TransitionSegment.(<br />
forall nctm5:new_copy_transition_map .<br />
forall nctm6:new_copy_transition_map . (<br />
nctm5.orig = nctm6.orig equiv nctm5.copy = nctm6.copy<br />
) <strong>and</strong><br />
forall cp_tsg:new_copy_transition_map . (<br />
exists nsm_elem_cptsg:new_state_map . (<br />
(is TransitionSegments(nsm_elem_cptsg.orig.one,<br />
82
cp_tsg.orig) or<br />
is TransitionSegments(nsm_elem_cptsg.orig.two,<br />
cp_tsg.orig)<br />
) <strong>and</strong><br />
result has TransitionSegments(nsm_elem_cptsg.copy,<br />
cp_tsg.copy)<br />
) or<br />
exists ncsm_elem_cptsg:new_copy_state_map . (<br />
is TransitionSegments(ncsm_elem_cptsg.orig, cp_tsg.orig) <strong>and</strong><br />
result has TransitionSegments(ncsm_elem_cptsg.copy,<br />
cp_tsg.copy)<br />
) or<br />
result has TransitionSegments(new_root_state, cp_tsg.copy)<br />
) <strong>and</strong><br />
/* build interfacepoints */<br />
exists new_ifpoint_map:map orig:interfacepoint_map<br />
to copy:new InterfacePoint . (<br />
forall nifpm5:new_ifpoint_map .<br />
forall nifpm6:new_ifpoint_map . (<br />
nifpm5.orig = nifpm6.orig equiv nifpm5.copy = nifpm6.copy<br />
) <strong>and</strong><br />
forall mk_ifp:new_ifpoint_map . (<br />
result has Name(mk_ifp.copy,<br />
concat(mk_ifp.orig.one.Name, mk_ifp.orig.two.Name)) <strong>and</strong><br />
exists new_dir:Direction . (<br />
result has Direction(mk_ifp.copy, new_dir) <strong>and</strong><br />
result has IsEntry(new_dir, mk_ifp.orig.one.Direction.IsEntry)<br />
) <strong>and</strong> (<br />
exists nsm_elem_mkifp:new_state_map . (<br />
(mk_ifp.orig.one.State = nsm_elem_mkifp.orig.one or<br />
mk_ifp.orig.two.State = nsm_elem_mkifp.orig.two<br />
) <strong>and</strong><br />
result has InterfacePoints(nsm_elem_mkifp.copy, mk_ifp.copy)<br />
)<br />
) <strong>and</strong><br />
forall ntsgm_mkifp:new_transition_map . (<br />
((ntsgm_mkifp.orig.one.SourcePoint = mk_ifp.orig.one or<br />
ntsgm_mkifp.orig.two.SourcePoint = mk_ifp.orig.two<br />
) <strong>and</strong><br />
result has SourcePoint(ntsgm_mkifp.copy, mk_ifp.copy)<br />
) or<br />
((ntsgm_mkifp.orig.one.DestinationPoint = mk_ifp.orig.one or<br />
ntsgm_mkifp.orig.two.DestinationPoint = mk_ifp.orig.two<br />
) <strong>and</strong><br />
result has DestinationPoint(ntsgm_mkifp.copy, mk_ifp.copy)<br />
) or true /* nothing to do for this transitionsegment */<br />
) <strong>and</strong><br />
forall nctsgm_mkifp:new_copy_transition_map . (<br />
((nctsgm_mkifp.orig.SourcePoint = mk_ifp.orig.one or<br />
83
nctsgm_mkifp.orig.SourcePoint = mk_ifp.orig.two<br />
) <strong>and</strong><br />
result has SourcePoint(nctsgm_mkifp.copy, mk_ifp.copy)<br />
) or<br />
((nctsgm_mkifp.orig.DestinationPoint = mk_ifp.orig.one or<br />
nctsgm_mkifp.orig.DestinationPoint = mk_ifp.orig.two<br />
) <strong>and</strong><br />
result has DestinationPoint(nctsgm_mkifp.copy, mk_ifp.copy)<br />
) or true /* nothing to do for this transitionsegment */<br />
)) <strong>and</strong><br />
/* copy interfacepoints */<br />
exists new_copy_ifpoint_map:map orig:copy_interfacepoint_set<br />
to copy:new InterfacePoint . (<br />
forall ncifpm5:new_copy_ifpoint_map .<br />
forall ncifpm6:new_copy_ifpoint_map . (<br />
ncifpm5.orig = ncifpm6.orig equiv ncifpm5.copy = ncifpm6.copy<br />
) <strong>and</strong><br />
forall cp_ifp:new_copy_ifpoint_map . (<br />
result has Name(cp_ifp.copy, cp_ifp.orig.Name) <strong>and</strong><br />
exists new_dir2:Direction . (<br />
result has Direction(cp_ifp.copy, new_dir2) <strong>and</strong><br />
result has IsEntry(new_dir2, cp_ifp.orig.Direction.IsEntry)<br />
) <strong>and</strong> (<br />
exists nsm_elem_cpifp:new_state_map . (<br />
(cp_ifp.orig.State = nsm_elem_cpifp.orig.one or<br />
cp_ifp.orig.State = nsm_elem_cpifp.orig.two<br />
) <strong>and</strong><br />
result has InterfacePoints(nsm_elem_cpifp.copy, cp_ifp.copy)<br />
) or<br />
exists ncsm_elem_cpifp:new_copy_state_map . (<br />
cp_ifp.orig.State = ncsm_elem_cpifp.orig <strong>and</strong><br />
result has InterfacePoints(ncsm_elem_cpifp.copy, cp_ifp.copy)<br />
)<br />
) <strong>and</strong><br />
forall ntsgm_cpifp:new_transition_map . (<br />
((ntsgm_cpifp.orig.one.SourcePoint = cp_ifp.orig or<br />
ntsgm_cpifp.orig.two.SourcePoint = cp_ifp.orig<br />
) <strong>and</strong><br />
result has SourcePoint(ntsgm_cpifp.copy, cp_ifp.copy)<br />
) or<br />
((ntsgm_cpifp.orig.one.DestinationPoint = cp_ifp.orig or<br />
ntsgm_cpifp.orig.two.DestinationPoint = cp_ifp.orig<br />
) <strong>and</strong><br />
result has DestinationPoint(ntsgm_cpifp.copy, cp_ifp.copy)<br />
) or true /* nothing to do for this transitionsegment */<br />
) <strong>and</strong><br />
forall nctsgm_cpifp:new_copy_transition_map . (<br />
(nctsgm_cpifp.orig.SourcePoint = cp_ifp.orig <strong>and</strong><br />
result has SourcePoint(nctsgm_cpifp.copy, cp_ifp.copy)<br />
84
) or<br />
(nctsgm_cpifp.orig.DestinationPoint = cp_ifp.orig <strong>and</strong><br />
result has DestinationPoint(nctsgm_cpifp.copy, cp_ifp.copy)<br />
) or true /* nothing to do for this transitionsegment */<br />
)) <strong>and</strong><br />
/* build locvariables */<br />
exists new_lvar_map:map orig:lvar_map to copy:new LocVariable . (<br />
forall nlvm1:new_lvar_map . forall nlvm2:new_lvar_map . (<br />
nlvm1.orig = nlvm2.orig equiv nlvm1.copy = nlvm2.copy<br />
) <strong>and</strong><br />
forall mk_lvar:new_lvar_map . (<br />
result has LocVariables(new_comp, mk_lvar.copy) <strong>and</strong><br />
result has Name(mk_lvar.copy,<br />
concat(mk_lvar.orig.one.Name, mk_lvar.orig.two.Name)) <strong>and</strong><br />
exists mk_lvar_mt:new MIFType . (<br />
result has DefinedType(mk_lvar.copy, mk_lvar_mt) <strong>and</strong><br />
exists ntm_elem_lvar:TConst . (<br />
mk_lvar.orig.one.DefinedType.Model = ntm_elem_lvar <strong>and</strong><br />
mk_lvar.orig.two.DefinedType.Model = ntm_elem_lvar <strong>and</strong><br />
result has Model(mk_lvar_mt, ntm_elem_lvar)<br />
))) <strong>and</strong><br />
/* copy locvariables */<br />
exists new_copy_lvar_map:map orig:copy_lvar_set<br />
to copy:new LocVariable . (<br />
forall nclvm1:new_copy_lvar_map .<br />
forall nclvm2:new_copy_lvar_map . (<br />
nclvm1.orig = nclvm2.orig equiv nclvm1.copy = nclvm2.copy<br />
) <strong>and</strong><br />
forall cp_lvar:new_copy_lvar_map . (<br />
result has LocVariables(new_comp, cp_lvar.copy) <strong>and</strong><br />
result has Name(cp_lvar.copy, cp_lvar.orig.Name) <strong>and</strong><br />
exists cp_lvar_mt:new MIFType . (<br />
result has DefinedType(cp_lvar.copy, cp_lvar_mt) <strong>and</strong><br />
result has Model(cp_lvar_mt, cp_lvar.orig.DefinedType.Model)<br />
)) <strong>and</strong><br />
/* build locavariableconsts */<br />
exists new_lvcn_map:map orig:lvarconst_map<br />
to copy:new LocVariableConst . (<br />
forall nlvcm1:new_lvcn_map .<br />
forall nlvcm2:new_lvcn_map . (<br />
nlvcm1.orig = nlvcm2.orig equiv nlvcm1.copy = nlvcm2.copy<br />
) <strong>and</strong><br />
forall mk_lvc:new_lvcn_map . (<br />
result has Name(mk_lvc.copy,<br />
concat(mk_lvc.orig.one.LocVariable.Name,<br />
mk_lvc.orig.two.LocVariable.Name)) <strong>and</strong><br />
exists nlvm_elem_lvcnm:new_lvar_map . (<br />
85
nlvm_elem_lvcnm.orig.one = mk_lvc.orig.one.LocVariable <strong>and</strong><br />
nlvm_elem_lvcnm.orig.two = mk_lvc.orig.two.LocVariable <strong>and</strong><br />
result has LocVariable(mk_lvc.copy, nlvm_elem_lvcnm.copy)<br />
)) <strong>and</strong><br />
/* copy locvariableconsts */<br />
exists new_copy_lvcn_map:map orig:copy_lvarconst_set<br />
to copy:new LocVariableConst . (<br />
forall nclvcm1:new_copy_lvcn_map .<br />
forall nclvcm2:new_copy_lvcn_map . (<br />
nclvcm1.orig = nclvcm2.orig equiv nclvcm1.copy = nclvcm2.copy<br />
) <strong>and</strong><br />
forall cp_lvc:new_copy_lvcn_map . (<br />
result has Name(cp_lvc.copy, cp_lvc.orig.LocVariable.Name) <strong>and</strong><br />
exists nclvm_elem_clvcnm:new_copy_lvar_map . (<br />
nclvm_elem_clvcnm.orig = cp_lvc.orig.LocVariable <strong>and</strong><br />
result has LocVariable(cp_lvc.copy, nclvm_elem_clvcnm.copy)<br />
)) <strong>and</strong><br />
/* build appls */<br />
exists new_appl_map:map orig:appl_map<br />
to copy:new Appl . (<br />
forall nappm1:new_appl_map .<br />
forall nappm2:new_appl_map . (<br />
nappm1.orig = nappm2.orig equiv nappm1.copy = nappm2.copy<br />
) <strong>and</strong><br />
forall mk_app:new_appl_map . (<br />
((mk_app.orig.one.Head = mk_app.orig.two.Head <strong>and</strong><br />
result has Head(mk_app.copy, mk_app.orig.one.Head)<br />
) or<br />
exists ncon_head:new_constructor_map . (<br />
mk_app.orig.one.Head = ncon_head.orig.one <strong>and</strong><br />
mk_app.orig.two.Head = ncon_head.orig.two <strong>and</strong><br />
result has Head(mk_app.copy, ncon_head.copy)<br />
)) <strong>and</strong><br />
forall param1:(mk_app.orig.one.Args) .<br />
exists param2:(mk_app.orig.two.Args) . (<br />
indexOf(param1, mk_app.orig.one, "Args") =<br />
indexOf(param2, mk_app.orig.two, "Args") <strong>and</strong><br />
(exists ncon_arg:new_constructor_map . (<br />
ncon_arg.orig.one = param1 <strong>and</strong><br />
ncon_arg.orig.two = param2 <strong>and</strong><br />
result has Args(mk_app.copy, ncon_arg.copy,<br />
indexOf(param1, mk_app.orig.one, "Args"))<br />
) or<br />
exists napp_arg:new_appl_map . (<br />
napp_arg.orig.one = param1 <strong>and</strong><br />
napp_arg.orig.two = param2 <strong>and</strong><br />
result has Args(mk_app.copy, napp_arg.copy,<br />
indexOf(param1, mk_app.orig.one, "Args"))<br />
86
) or<br />
exists nlvar_arg:new_lvcn_map . (<br />
nlvar_arg.orig.one = param1 <strong>and</strong><br />
nlvar_arg.orig.two = param2 <strong>and</strong><br />
result has Args(mk_app.copy, nlvar_arg.copy,<br />
indexOf(param1, mk_app.orig.one, "Args"))<br />
) or<br />
(param1 = param2 <strong>and</strong><br />
result has Args(mk_app.copy, param1,<br />
indexOf(param1, mk_app.orig.one, "Args"))<br />
)))) <strong>and</strong><br />
/* copy appls */<br />
exists new_copy_appl_map:map orig:copy_appl_set<br />
to copy:new Appl . (<br />
forall ncappm1:new_copy_appl_map .<br />
forall ncappm2:new_copy_appl_map . (<br />
ncappm1.orig = ncappm2.orig equiv ncappm1.copy = ncappm2.copy<br />
) <strong>and</strong><br />
forall cp_app:new_copy_appl_map . (<br />
(exists ncon_head2:new_constructor_map . (<br />
(cp_app.orig.Head = ncon_head2.orig.one or<br />
cp_app.orig.Head = ncon_head2.orig.two<br />
) <strong>and</strong><br />
result has Head(cp_app.copy, ncon_head2.copy)<br />
) or<br />
/* normal const */<br />
result has Head(cp_app.copy, cp_app.orig.Head)<br />
) <strong>and</strong><br />
forall param:(cp_app.orig.Args) . (<br />
exists ncapp_arg:new_copy_appl_map . (<br />
ncapp_arg.orig = param <strong>and</strong><br />
result has Args(cp_app.copy, ncapp_arg.copy,<br />
indexOf(param, cp_app.orig, "Args"))<br />
) or<br />
exists ncon_arg2:new_constructor_map . (<br />
(ncon_arg2.orig.one = param or<br />
ncon_arg2.orig.two = param<br />
) <strong>and</strong><br />
result has Args(cp_app.copy, ncon_arg2.copy,<br />
indexOf(param, cp_app.orig, "Args"))<br />
) or<br />
exists nlvar_arg2:new_lvcn_map . (<br />
(nlvar_arg2.orig.one = param or<br />
nlvar_arg2.orig.two = param<br />
) <strong>and</strong><br />
result has Args(cp_app.copy, nlvar_arg2.copy,<br />
indexOf(param, cp_app.orig, "Args"))<br />
) or<br />
exists nlvarcopy_arg:new_copy_lvcn_map . (<br />
87
nlvarcopy_arg.orig = param <strong>and</strong><br />
result has Args(cp_app.copy, nlvarcopy_arg.copy,<br />
indexOf(param, cp_app.orig, "Args"))<br />
) or<br />
/* other Const incl. not-unified Constructors */<br />
result has Args(cp_app.copy, param,<br />
indexOf(param, cp_app.orig, "Args"))<br />
)) <strong>and</strong><br />
/* build inputs */<br />
exists new_input_map:map orig:input_map to copy:new Input . (<br />
forall nim1:new_input_map . forall nim2:new_input_map . (<br />
nim1.orig = nim2.orig equiv nim1.copy = nim2.copy<br />
) <strong>and</strong><br />
forall mk_inp:new_input_map . (<br />
exists ntm_elem:new_transition_map . (<br />
ntm_elem.orig.one = mk_inp.orig.one.TransitionSegment <strong>and</strong><br />
ntm_elem.orig.two = mk_inp.orig.two.TransitionSegment <strong>and</strong><br />
result has TransitionSegment(mk_inp.copy, ntm_elem.copy)<br />
) <strong>and</strong><br />
exists npm_elem_inp:new_port_map . (<br />
npm_elem_inp.orig.one = mk_inp.orig.one.Port.Model <strong>and</strong><br />
npm_elem_inp.orig.two = mk_inp.orig.two.Port.Model <strong>and</strong><br />
exists new_port_inp:new MIFPort . (<br />
result has Port(mk_inp.copy, new_port_inp) <strong>and</strong><br />
result has Model(new_port_inp, npm_elem_inp.copy)<br />
)) <strong>and</strong><br />
exists nmt_inp:new MIFTerm . (<br />
result has Pattern(mk_inp.copy, nmt_inp) <strong>and</strong><br />
run setUnifiedMIFTerm(mk_inp.orig.one.Pattern,<br />
mk_inp.orig.two.Pattern, nmt_inp, new_constructor_map,<br />
new_appl_map, new_lvcn_map)<br />
))) <strong>and</strong><br />
/* copy inputs */<br />
exists new_copy_input_map:map orig:copy_input_set<br />
to copy:new Input . (<br />
forall ncim1:new_copy_input_map .<br />
forall ncim2:new_copy_input_map . (<br />
ncim1.orig = ncim2.orig equiv ncim1.copy = ncim2.copy<br />
) <strong>and</strong><br />
forall cp_inp:new_copy_input_map . (<br />
(exists ntm_elem_inp2:new_transition_map . (<br />
(ntm_elem_inp2.orig.one = cp_inp.orig.TransitionSegment or<br />
ntm_elem_inp2.orig.two = cp_inp.orig.TransitionSegment<br />
) <strong>and</strong><br />
result has TransitionSegment(cp_inp.copy, ntm_elem_inp2.copy)<br />
) or<br />
exists nctm_elem_inp:new_copy_transition_map . (<br />
nctm_elem_inp.orig = cp_inp.orig.TransitionSegment <strong>and</strong><br />
88
esult has TransitionSegment(cp_inp.copy, nctm_elem_inp.copy)<br />
)) <strong>and</strong> (<br />
exists npm_elem_inp2:new_port_map . (<br />
(npm_elem_inp2.orig.one = cp_inp.orig.Port.Model or<br />
npm_elem_inp2.orig.two = cp_inp.orig.Port.Model<br />
) <strong>and</strong><br />
exists new_port_cinp:new MIFPort . (<br />
result has Port(cp_inp.copy, new_port_cinp) <strong>and</strong><br />
result has Model(new_port_cinp, npm_elem_inp2.copy)<br />
)) or<br />
exists ncpm_elem_inp:new_copy_port_map . (<br />
ncpm_elem_inp.orig = cp_inp.orig.Port.Model <strong>and</strong><br />
exists new_port_inp2:new MIFPort . (<br />
result has Port(cp_inp.copy, new_port_inp2) <strong>and</strong><br />
result has Model(new_port_inp2, ncpm_elem_inp.copy)<br />
))) <strong>and</strong><br />
exists nmt_cinp:new MIFTerm . (<br />
result has Pattern(cp_inp.copy, nmt_cinp) <strong>and</strong><br />
run setCopiedMIFTerm(cp_inp.orig.Pattern, nmt_cinp,<br />
new_constructor_map, new_copy_constructor_map, new_appl_map,<br />
new_copy_appl_map, new_lvcn_map, new_copy_lvcn_map)<br />
))) <strong>and</strong><br />
/* build outputs */<br />
exists new_output_map:map orig:output_map to copy:new Output . (<br />
forall nom1:new_output_map . forall nom2:new_output_map . (<br />
nom1.orig = nom2.orig equiv nom1.copy = nom2.copy<br />
) <strong>and</strong><br />
forall mk_outp:new_output_map . (<br />
exists ntm_elem_outp:new_transition_map . (<br />
ntm_elem_outp.orig.one=mk_outp.orig.one.TransitionSegment <strong>and</strong><br />
ntm_elem_outp.orig.two=mk_outp.orig.two.TransitionSegment <strong>and</strong><br />
result has TransitionSegment(mk_outp.copy, ntm_elem_outp.copy)<br />
) <strong>and</strong><br />
exists npm_elem_outp:new_port_map . (<br />
npm_elem_outp.orig.one = mk_outp.orig.one.Port.Model <strong>and</strong><br />
npm_elem_outp.orig.two = mk_outp.orig.two.Port.Model <strong>and</strong><br />
exists new_port_outp:new MIFPort . (<br />
result has Port(mk_outp.copy, new_port_outp) <strong>and</strong><br />
result has Model(new_port_outp, npm_elem_outp.copy)<br />
)<br />
) <strong>and</strong><br />
exists nmt_outp:new MIFTerm . (<br />
result has Expression(mk_outp.copy, nmt_outp) <strong>and</strong><br />
run setUnifiedMIFTerm(mk_outp.orig.one.Expression,<br />
mk_outp.orig.two.Expression, nmt_outp, new_constructor_map,<br />
new_appl_map, new_lvcn_map)<br />
))) <strong>and</strong><br />
/* copy outputs */<br />
89
exists new_copy_output_map:map orig:copy_output_set<br />
to copy:new Output . (<br />
forall ncom1:new_copy_output_map .<br />
forall ncom2:new_copy_output_map . (<br />
ncom1.orig = ncom2.orig equiv ncom1.copy = ncom2.copy<br />
) <strong>and</strong><br />
forall cp_outp:new_copy_output_map . (<br />
(exists ntm_elem_outp2:new_transition_map . (<br />
(ntm_elem_outp2.orig.one = cp_outp.orig.TransitionSegment or<br />
ntm_elem_outp2.orig.two = cp_outp.orig.TransitionSegment<br />
) <strong>and</strong><br />
result has<br />
TransitionSegment(cp_outp.copy, ntm_elem_outp2.copy)<br />
) or<br />
exists nctm_elem_outp:new_copy_transition_map . (<br />
nctm_elem_outp.orig = cp_outp.orig.TransitionSegment <strong>and</strong><br />
result has<br />
TransitionSegment(cp_outp.copy, nctm_elem_outp.copy)<br />
)) <strong>and</strong> (<br />
exists npm_elem_outp2:new_port_map . (<br />
(npm_elem_outp2.orig.one = cp_outp.orig.Port.Model or<br />
npm_elem_outp2.orig.two = cp_outp.orig.Port.Model<br />
) <strong>and</strong><br />
exists new_port_coutp:new MIFPort . (<br />
result has Port(cp_outp.copy, new_port_coutp) <strong>and</strong><br />
result has Model(new_port_coutp, npm_elem_outp2.copy)<br />
)) or<br />
exists ncpm_elem_outp:new_copy_port_map . (<br />
ncpm_elem_outp.orig = cp_outp.orig.Port.Model <strong>and</strong><br />
exists new_port_outp2:new MIFPort . (<br />
result has Port(cp_outp.copy, new_port_outp2) <strong>and</strong><br />
result has Model(new_port_outp2, ncpm_elem_outp.copy)<br />
))) <strong>and</strong><br />
exists nmt_coutp:new MIFTerm . (<br />
result has Expression(cp_outp.copy, nmt_coutp) <strong>and</strong><br />
run setCopiedMIFTerm(cp_outp.orig.Expression, nmt_coutp,<br />
new_constructor_map, new_copy_constructor_map, new_appl_map,<br />
new_copy_appl_map, new_lvcn_map, new_copy_lvcn_map)<br />
))) <strong>and</strong><br />
/* build actions */<br />
exists new_action_map:map orig:action_map to copy:new Action . (<br />
forall nactm1:new_action_map . forall nactm2:new_action_map . (<br />
nactm1.orig = nactm2.orig equiv nactm1.copy = nactm2.copy<br />
) <strong>and</strong><br />
forall mk_act:new_action_map . (<br />
exists ntm_elem_act:new_transition_map . (<br />
ntm_elem_act.orig.one = mk_act.orig.one.TransitionSegment <strong>and</strong><br />
ntm_elem_act.orig.two = mk_act.orig.two.TransitionSegment <strong>and</strong><br />
result has TransitionSegment(mk_act.copy, ntm_elem_act.copy)<br />
90
) <strong>and</strong><br />
exists npm_elem_act:new_lvar_map . (<br />
npm_elem_act.orig.one = mk_act.orig.one.LocVariable.Model <strong>and</strong><br />
npm_elem_act.orig.two = mk_act.orig.two.LocVariable.Model <strong>and</strong><br />
exists new_lvcn_act:new MIFLocVariable . (<br />
result has LocVariable(mk_act.copy, new_lvcn_act) <strong>and</strong><br />
result has Model(new_lvcn_act, npm_elem_act.copy)<br />
)) <strong>and</strong><br />
exists nmt_act:new MIFTerm . (<br />
result has Value(mk_act.copy, nmt_act) <strong>and</strong><br />
run setUnifiedMIFTerm(mk_act.orig.one.Value,<br />
mk_act.orig.two.Value, nmt_act, new_constructor_map,<br />
new_appl_map, new_lvcn_map)<br />
))) <strong>and</strong><br />
/* copy actions */<br />
exists new_copy_action_map:map orig:copy_action_set<br />
to copy:new Action . (<br />
forall ncactm1:new_copy_action_map .<br />
forall ncactm2:new_copy_action_map . (<br />
ncactm1.orig = ncactm2.orig equiv ncactm1.copy = ncactm2.copy<br />
) <strong>and</strong><br />
forall cp_act:new_copy_action_map . (<br />
(exists ntm_elem_act2:new_transition_map . (<br />
(ntm_elem_act2.orig.one = cp_act.orig.TransitionSegment or<br />
ntm_elem_act2.orig.two = cp_act.orig.TransitionSegment<br />
) <strong>and</strong><br />
result has TransitionSegment(cp_act.copy, ntm_elem_act2.copy)<br />
) or<br />
exists nctm_elem_act:new_copy_transition_map . (<br />
nctm_elem_act.orig = cp_act.orig.TransitionSegment <strong>and</strong><br />
result has TransitionSegment(cp_act.copy, nctm_elem_act.copy)<br />
)) <strong>and</strong> (<br />
exists npm_elem_act2:new_lvar_map . (<br />
(npm_elem_act2.orig.one = cp_act.orig.LocVariable.Model or<br />
npm_elem_act2.orig.two = cp_act.orig.LocVariable.Model<br />
) <strong>and</strong><br />
exists new_lvcn_cact:new MIFLocVariable . (<br />
result has LocVariable(cp_act.copy, new_lvcn_cact) <strong>and</strong><br />
result has Model(new_lvcn_cact, npm_elem_act2.copy)<br />
)) or<br />
exists ncpm_elem_act:new_copy_lvar_map . (<br />
ncpm_elem_act.orig = cp_act.orig.LocVariable.Model <strong>and</strong><br />
exists new_lvcn_act2:new MIFLocVariable . (<br />
result has LocVariable(cp_act.copy, new_lvcn_act2) <strong>and</strong><br />
result has Model(new_lvcn_act2, ncpm_elem_act.copy)<br />
))) <strong>and</strong><br />
exists nmt_cact:new MIFTerm . (<br />
result has Value(cp_act.copy, nmt_cact) <strong>and</strong><br />
run setCopiedMIFTerm(cp_act.orig.Value, nmt_cact,<br />
91
new_constructor_map, new_copy_constructor_map, new_appl_map,<br />
new_copy_appl_map, new_lvcn_map, new_copy_lvcn_map)<br />
))) <strong>and</strong><br />
/* build conditions */<br />
exists new_condition_map:map orig:condition_map<br />
to copy:new Condition . (<br />
forall ncondm1:new_condition_map .<br />
forall ncondm2:new_condition_map . (<br />
ncondm1.orig = ncondm2.orig equiv ncondm1.copy = ncondm2.copy<br />
) <strong>and</strong><br />
forall mk_cond:new_condition_map . (<br />
exists ntm_elem_cond:new_transition_map . (<br />
ntm_elem_cond.orig.one=mk_cond.orig.one.TransitionSegment <strong>and</strong><br />
ntm_elem_cond.orig.two=mk_cond.orig.two.TransitionSegment <strong>and</strong><br />
result has TransitionSegment(mk_cond.copy, ntm_elem_cond.copy)<br />
) <strong>and</strong><br />
exists nmt_cond:new MIFTerm . (<br />
result has Expression(mk_cond.copy, nmt_cond) <strong>and</strong><br />
run setUnifiedMIFTerm(mk_cond.orig.one.Expression,<br />
mk_cond.orig.two.Expression, nmt_cond, new_constructor_map,<br />
new_appl_map, new_lvcn_map)<br />
))) <strong>and</strong><br />
/* copy conditions */<br />
exists new_copy_condition_map:map orig:copy_condition_set<br />
to copy:new Condition . (<br />
forall nccondm1:new_copy_condition_map .<br />
forall nccondm2:new_copy_condition_map . (<br />
nccondm1.orig=nccondm2.orig equiv nccondm1.copy=nccondm2.copy<br />
) <strong>and</strong><br />
forall cp_cond:new_copy_condition_map . (<br />
(exists ntm_elem_cond2:new_transition_map . (<br />
(ntm_elem_cond2.orig.one = cp_cond.orig.TransitionSegment or<br />
ntm_elem_cond2.orig.two = cp_cond.orig.TransitionSegment<br />
) <strong>and</strong><br />
result has<br />
TransitionSegment(cp_cond.copy, ntm_elem_cond2.copy)<br />
) or<br />
exists nctm_elem_cond:new_copy_transition_map . (<br />
nctm_elem_cond.orig = cp_cond.orig.TransitionSegment <strong>and</strong><br />
result has<br />
TransitionSegment(cp_cond.copy, nctm_elem_cond.copy)<br />
)) <strong>and</strong><br />
exists nmt_ccond:new MIFTerm . (<br />
result has Expression(cp_cond.copy, nmt_ccond) <strong>and</strong><br />
run setCopiedMIFTerm(cp_cond.orig.Expression, nmt_ccond,<br />
new_constructor_map, new_copy_constructor_map, new_appl_map,<br />
new_copy_appl_map, new_lvcn_map, new_copy_lvcn_map)<br />
)))<br />
))))))))))))))))))))))<br />
92
Literaturverzeichnis<br />
[Dun05] Dennis Dungs. Design <strong>and</strong> implementation of a unique interface<br />
<strong>and</strong> object model for the integration of supplementary tools in common<br />
UML modeling tools. Systementwicklungsprojekt, Technische<br />
Universität München, 2005.<br />
[Höl05]<br />
[HS05]<br />
Florian Hölzl. Erweiterung und Optimierung des AQuA-<strong>Systems</strong>.<br />
Systementwicklungsprojekt Projektbericht, Technische Universität<br />
München, 2005.<br />
Florian Hölzl <strong>and</strong> Bernhard Schätz. Adding interaction <strong>and</strong> transformation<br />
to constraint languages. Zur Zeit der Erstellung, eingereicht<br />
bei der Konferenz MoDELS 2005, 2005.<br />
[OMG03a] OMG. OMG Unified Modeling Language Specification.<br />
http://www.omg.org, March 2003.<br />
[OMG03b] OMG. UML 2.0 OCL Specification. http://www.omg.org, 2003.<br />
[OMG04] OMG. MetaObject Facility (MOF) Specification.<br />
http://www.omg.org, Apr. 2004.<br />
[Pas05]<br />
David Pasch. Modellbasierte Prozessunterstützung für AutoFocus.<br />
Diplomarbeit, Technische Universität München, 2005.<br />
[Sch00] Uwe Schöning. Logik für Informatiker. Spektrum Akademischer<br />
Verlag, 2000.<br />
[Sch01]<br />
[Str05]<br />
[Tra03]<br />
Bernhard Schätz. The ODL Operation Definition Language <strong>and</strong> the<br />
AutoFocus/Quest application framework AQuA. Technical Report<br />
<strong>TUM</strong>-I0111, Technische Universität München, 2001.<br />
Markus Strohmeier. Modellbasierte Validierung verteilter Komponenten:<br />
Kopplung von AutoFOCUS und SPIN. Diplomarbeit, Technische<br />
Universität München, 2005.<br />
David Trachtenherz. Erweiterung des AQuA-<strong>Systems</strong>: ODL Sprachkonstrukte<br />
und interaktive Benutzerschnittstelle. Diplomarbeit,<br />
Technische Universität München, 2003.<br />
[<strong>TUM</strong>02] AutoFocus. http://autofocus.in.tum.de/, 2002.<br />
[<strong>TUM</strong>04a] AutoMode. http://www4.in.tum.de/∼automode, 2004.<br />
93
[<strong>TUM</strong>04b] AutoRaid. http://www4.in.tum.de/∼autoraid, 2004.<br />
[<strong>TUM</strong>05] AutoFocus 2. http://www4.in.tum.de/∼af2, 2005.<br />
[W3C99] W3C. XSL Transformation (XSLT) Version 1.0.<br />
http://www.w3c.org, November 1999.<br />
94