Mir war einige Zeit unklar, wie ich beginnen soll, auf programmatischem Weg eine .uml-Datei mittels des UML2-Plugins von Eclipse zu erzeugen.

Zunächst einmal ein paar Infos zu Eclipse-UML2:

UML2 is an EMF-based implementation of the Unified Modeling Language (UMLTM) 2.x OMG metamodel for the Eclipse platform.

The objectives of the UML2 component are to provide

  • a useable implementation of the UML metamodel to support the development of modeling tools
  • a common XMI schema to facilitate interchange of semantic models
  • test cases as a means of validating the specification
  • validation rules as a means of defining and enforcing levels of compliance

For more details on UML2, see the Wiki.

Quelle: http://www.eclipse.org/modeling/mdt/?project=uml2

Erste Schritte – aber wie?

Ein hilfreicher Artikel war Getting Started with UML2, der auf zwei Wegen erklärt, wie man eine .uml-Datei mit Elementen wie Package, Class, etc. … erstellt; namlich einmal per mitgeliefertem Editor, bei dem die UML-Elemente in einer Baumstruktur angezeigt werden – so wie man das auch aus vielen grafischen Modellierungseditoren kennt – und einmal per Java-Code.

“Per Java-Code” war genau das was ich gesucht habe. Nachdem ich mir die Code-Schnipsel und Erklärungen angesehen hatte, stellte sich mir die Frage, wie ich das selbst nachbauen konnte.

Ich erstellte in Eclipse also ein neues Java-Projekt und fügte einfach mal einen Code-Schnipsel in die main(String[] args)-Methode ein. Natürlich meckerte Eclipse sofort, da es ja Klassen wie z.B. “Model” nicht kannte und ich stand vor dem Rätsel, welche Programmbibliotheken in den Build-Path müssen und wo diese zu finden sind. Im “[eclipse-root]/plugin”-Ordner wurde ich fündig. Allerdings war es mühsam, die ganzen Abhängigkeiten per Hand aufzulösen, doch ich brachte mein Code-Beispiel zum laufen und erzeugte so eine Instanz der Klasse “Model” irgendwo in den unendlichen Weiten meines Arbeitsspeichers.

Abhängigkeitsverwaltung mit Maven

Da ich keine Lust hatte, mich um die ganzen Abhängigkeiten zu kümmern, beschloss ich, diesen Teil der Arbeit an Apache Maven abzudrücken und erstellte ein neues Maven-Projekt. (Voraussetzung ist natürlich das Maven2-Plugin in Eclipse installiert zu haben.)

Maven ist ein Build-Management-Tool der Apache Software Foundation und basiert auf Java. Mit ihm kann man insbesondere Java-Programme standardisiert erstellen und verwalten.

[…]

Auflösung von Abhängigkeiten, zentrale Repositories

In der pom.xml werden Softwareabhängigkeiten angegeben, die ein von Maven unterstütztes Softwareprojekt zu anderen Softwareprojekten hat. Diese Abhängigkeiten werden aufgelöst, indem Maven zunächst ermittelt, ob die benötigten Dateien in einem lokalen Verzeichnis, dem lokalen Maven-Repository, bereits vorhanden sind. Sind sie es, verwendet Maven z. B. beim Kompilieren die lokal vorhandene Datei von dort, also ohne sie in das Projektverzeichnis zu kopieren. Kann die Abhängigkeit nicht lokal aufgelöst werden, versucht Maven, sich mit einem Maven-Repository im Internet zu verbinden und von dort die Dateien in das lokale Repository zu kopieren, um sie von nun an lokal verwenden zu können.

Quelle: http://de.wikipedia.org/wiki/Apache_Maven

Hier der Inhalt meiner pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>DA_PreResearch</groupId>
  <artifactId>DA_PreResearch</artifactId>
  <name />
  <version>0.1</version>
  <description />
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.eclipse.uml2</groupId>
      <artifactId>uml</artifactId>
      <version>2.1.0-v200706251652</version>
    </dependency>
  </dependencies>
</project>

Maven Dependency Graph

Maven Dependency Graph

Wie man sehen kann, reicht ein dependency-Eintrag aus, da Maven sich um die restlichen Abhängigkeiten kümmert

Die nebenstehende Abbildung zeigt die restlichen Abhängigkeiten, die von Maven aufgelöst wurden.

Der von mir eingeschlagene Weg beschreibt, wie man Eclipse-UML2 in einer “Standalone”-Applikation verwendet. Welche Vorgehensweise für die Eclipse-Plugin-Entwicklung notwendig ist weiß ich (noch) nicht. Für Tips, Anregungen, Hinweise hierzu wäre ich dankbar :)

Erste Erfolge

Schnell gelang es mir, nach den vorangegangenen Schritten, die einiges an Rechercheaufwand benötigten, weitere Erfolge zu erzielen. So schrieb ich ein kleines Programm, das nicht nur eine Instanz der Klasse “Model” erzeugt, sondern diesem auch ein “Package” sowie eine “Class” hinzufügt. Das war ja alles schön und gut, doch diese transienten Daten gingen nach Programmbeendigung immer wieder flöten, so dass ich das Programm noch ergänzte, indem ich eine Methode hinzufügte, die mir das Programm serialisiert und in eine Datei schreibt.

Die Vorgehensweise hierzu war zwar im oben genannten Artikel beschrieben, doch ich erhielt zur Laufzeit immer eine NullPointerException. Erst ein Newsgroup-Post brachte mich auf den richtigen Weg. Ich änderte den aus dem Artikel übernommenen Code-Schnipsel entsprechend ab und siehe da, ich erhielt eine schöne kleine .uml-Datei. Hier der Inhalt dieser Datei:

<?xml version="1.0" encoding="ASCII"?>
<uml:Model xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uml="http://www.eclipse.org/uml2/2.1.0/UML" name="Data">
  <packagedElement xsi:type="uml:Package" name="TravelBookingSystem">
    <packagedElement xsi:type="uml:Class" name="TravelBookingSystem"/>
  </packagedElement>
</uml:Model>

Schreiben klappt, was ist mit lesen?

Leider wurde dieser Teil nicht in dem Artikel behandelt und ich musste mich nach anderen Informationsquellen umsehen. Hier half mir schließlich das Javadoc von org.eclipse.uml2.uml weiter. Es ist zwar nicht so ausführlich wie das von EMF, aber um einen Einblick in die Struktur zu erhalten und Zusammenhänge der Funktionen zu verstehen reicht es allemal.

Mir gelang es schließlich die in den vorigen Schritten erstellte Datei zu öffnen und auszulesen. Dabei boten sich mir zwei Methoden an, einmal “getAllContents()”, das mir einen Baumiterator mit allen Elementen lieferte und einmal eine Liste aller Elemente auf oberster Ebene.

Hier gibts den Quellcode zum Download: UML2Example.java

So lauten die Methodenaufrufe, mit denen ich die .uml-Datei erzeuge und wieder auslese:
UML2Example.init();
UML2Example.createUML2File("TravelBookingSystem", "");
UML2Example.readUML2File("TravelBookingSystem", "");

Und dies ist die Ausgabe in der Konsole:
Model 'Data' created.
Package 'Data::TravelBookingSystem' created.
Class 'Data::TravelBookingSystem::TravelBookingSystem' created.
Done.
Content in TreeIterator: org.eclipse.uml2.uml.internal.impl.ModelImpl@1884a40 (name: Data, visibility: ) (visibility: public) (viewpoint: )
Content in TreeIterator: org.eclipse.uml2.uml.internal.impl.PackageImpl@1bfc4fc (name: TravelBookingSystem, visibility: ) (visibility: public)
Content in TreeIterator: org.eclipse.uml2.uml.internal.impl.ClassImpl@1a1399 (name: TravelBookingSystem, visibility: ) (isLeaf: false, visibility: public, isAbstract: false) (isActive: false, isAbstract: false)
Content in EList: org.eclipse.uml2.uml.internal.impl.ModelImpl@1884a40 (name: Data, visibility: ) (visibility: public) (viewpoint: )
--> This element is instance of class 'ModelImpl' that implements interface 'Model'.
--> owned Element: org.eclipse.uml2.uml.internal.impl.PackageImpl@1bfc4fc (name: TravelBookingSystem, visibility: ) (visibility: public)
--> owned Element: org.eclipse.uml2.uml.internal.impl.ClassImpl@1a1399 (name: TravelBookingSystem, visibility: ) (isLeaf: false, visibility: public, isAbstract: false) (isActive: false, isAbstract: false)

Fazit

Nach den gemachten Erfahrungen war ich in der Lage eine .uml-Datei auszulesen und den Inhalt weiterzuverarbeiten. Jetzt könnte ich mittels XPath die Elemente aus dem Quellmodell identifizieren und dann programmatisch weiterverarbeiten und so das Zielmodell erstellen.

Ich denke für die SUM->View-Transformation wird sich einfacher erweisen, als die umgekehrte Richtung, da bei jedem Aufruf ein neues Modell erzeugt werden kann. Bei der View->SUM-Transformation muss man in der View gemachte Änderungen in das SUM übernehmen und somit ein bestehendes Modell bearbeiten. Der dazu notwendige Algorithmus könnte sich als kompliziert erweisen, aber das ist bis jetzt nur eine Vermutung.

Bis jetzt denke ich, dass die programmatische Transformation ziemlich gut zu bewerkstelligen ist. Der nächste Schritt wird die Programmierung der Beispieltransformation sein, nach der ich weitere Erkenntnisse haben werde und eine präzisere Beurteilung dieses Transformationsansatzes geben kann.

@Benjamin: Ich bin gespannt auf deine Ergebnisse des Ansatzes, die Transformationen per ATL durchzuführen.