03.02.2014 Aufrufe

Spezifikationsmodule - Software and Systems Engineering - TUM

Spezifikationsmodule - Software and Systems Engineering - TUM

Spezifikationsmodule - Software and Systems Engineering - TUM

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!