GMF
Das Graphical Modeling Framework GMF versteht sich an generative Brücke zwischen EMF und GEF zur Erzeugung graphischer Editoren.
Der Einstieg in GMF wird vor allem durch die Tutorials im GMF-Wiki erleichtert.
Ein weiteres sehr schönes Tutorial findet sich unter GMF Tutorial von IBM
Eine selbsterstellte Übersicht über die wichtigsten GMF Elemente findet sich zudem hier: Datei:GMF-Doku.pdf.
Customizations (GMF 2.0 M5)
Viele Editoren benötigen einiges an Anpassungen, um wirklich schön zu sein.
Ein sehr schönes Tutorial dazu findet sich unter EclipseCon2007.
Anpassungen am Generator-Mark-Modell ermöglichen auch vielfältige Customisation, die man immer gut gebrauchen kann. Eine Anleitung zu den vielen Optionen findet sich hier [1]
Mehrere Diagramme auf einer einzigen Editing Domain
Was ist hier die Idee? Eine Editing Domain enthält das Modell sowie den Command-Stack, der durch seine Kommandos auf dem Modell operiert. Nutzt man nun den GMF Generator, so erstellt dieser für jedes Diagramm-Plugin eine eigene EditingDomain. Was ist die Konsequenz davon? Nun, da nun jeder Editor seine eigene Domain hat, hat auch jeder seine eigene Kopie des Modells. Mehrere Views auf ein und dasselbe Modell sind damit unmöglich. Das betrifft sowohl mehrere Diagramme für eine Modellinstanz als auch mehrere Nicht-Diagramm-Views gemischt mit Diagrammen, die alle _synchrone_ Informationen zeigen. Beispiele in der PCMBench sind der Navigator View und die Diagramme, besonders das Repository- und das SEFF-Diagramm, da diese sogar direkt auf der selben Modellinstanz arbeiten können müssen. Was ist also zu tun, damit GMF mit einer einzigen EditingDomain auskommt?
Nun, zum Glück hat das wohl schon mal jemand gefragt und einen Artikel im GMF Wiki angelegt. Das ist sehr schön, jedoch auch dumm, weil die Anleitung nur mit GMF1.x funktioniert. Was nötig ist, damit es auch mit GMF2.0 (oder auch dem Nachbarn) klappt kommt hier:
- Zuerst: Die Anleitung aus dem GMF Wiki durchführen. Im Großen und Ganzen stimmt die noch.
- Dann in den generierten ??DiagramEditor Klassen noch die Methode
@Override protected String getEditingDomainID() { return "de.uka.ipd.sdq.PCMBench.editingDomain"; }
einfügen. Der zurückgegebene String muss die ID der gemeinsamen Editing Domain sein.
- In der generierten Datei ??DocumentProvider verbirgt sich der nächste Stolperstein. Dort wird (aus mir absolut unverständlichen Gründen, Buganfrage läuft) eine weitere EditingDomain erzeugt. Das ist natürlich in unserem Kontext Käse. Daher die ersten Zeilen der Methode ersetzen und @generated auf @generated NOT ändern
/** * @generated NOT */ private TransactionalEditingDomain createEditingDomain() { // TransactionalEditingDomain editingDomain = DiagramEditingDomainFactory // .getInstance().createEditingDomain(); // editingDomain.setID("de.uka.ipd.sdq.PCMBench.editingDomain"); //$NON-NLS-1$ TransactionalEditingDomain editingDomain = TransactionalEditingDomain.Registry.INSTANCE .getEditingDomain("de.uka.ipd.sdq.PCMBench.editingDomain"); [..]
- Und zum Schluss: Die Extension für die geteilte EditingDomain verlangt nach einer Factory-Klasse. Hierfür muss die GMF-Factory eingesetzt werden. Also
<extension point="org.eclipse.emf.transaction.editingDomains"> <editingDomain factory="org.eclipse.gmf.runtime.diagram.core.DiagramEditingDomainFactory" id="de.uka.ipd.sdq.PCMBench.editingDomain"/> </extension>
in der plugin.xml des Plugins, dass die Editing Domain bereitstellt.
Nun sollte es klappen...
Elemente konfigurieren während sie angelegt werden
Ein übliches Problem ist es, Element bei ihrer Erzeugung zu konfigurieren. Ein Beispiel aus unserem Kontext ist z.B. der externe Dienstaufrufe, bei denen die aufgerufene Methode abgefragt werden muss, bevor der Dienstaufruf komplett ist. Dies wurde bisher durch patchen des generierten Codes erreicht, indem dort die generierten Create???Command Klassen angepasst wurden, indem die Methode doExecuteWithResult oder doDefaultElementCreation mit den entsprechenden Abfragen angereichert wurden. Das hat allerdings Nachteile:
- Der generierte Code wird gehackt, ein Neugenerieren wird schwieriger und fehleranfällig (Stichwort: Vergessenes @generated not)
- Generierter und nicht-generierter Code vermischen sich in einem Plugin
- Es gibt für verschiedene Diagramm-Hierachiestufen verschiedene Create???Command Klassen, mehrfache Anpassungen sind also nötig
Es geht aber auch anders und wohl auch eher so, wie es von den GMF Entwicklern geplant war. Es können so genannte EditHelper bzw. EditHelperAdvices eingetzt werden. Die Idee hierbei geht über die ElementTypeRegistry. GMF hält für jedes EMF-Domänen-Metamodell eine Registry, in der alle Metaklassen registriert sind (siehe Extension Point elementTypes in der plugin.xml eines generierten Editors). Diese Registry kann aber noch mehr. Es können z.B. die Meta-Klassen spezialisiert (vererbt) werden, damit sie auf verschiedenen Diagrammen oder auch auf einem Diagramm an verschiedenen Stellen unterschiedlich aussehen und sich unterschiedlich verhalten. Fürs aussehen sorgt in der Registry der Semantik Hint, der das zugehörige View identifiziert. Fürs Verhalten sind die EditHelper (bei echten Metaklassen) bzw. die EditHelperAdvices (bei Spezialisierungen und bei reinen Advices) zuständig. Die Idee gleicht dem Decorator Design Muster oder vielen AOP Ansätzen: Immer wenn etwas über die TypeRegistry angefordert wird (z.B. das Anlegen eines Domain-Modell-Elements) wird der EditHelper befragt, ob er noch zusätliche Dinge ausführen will. Dieser wiederrum fragt alle bei ihm registrierten EditHelperAdvices - und zwar vor (BeforeAdvice), während (InsteadAdvice) und nach (After) jeder Aktion, ob sie noch was tun möchten. Das eignet sich natürlich hervorragend für den erwähnten Use-Case. Das setzen von Felder von neu angelegten Elementen kann man nun z.B. in einem AfterConfigureCommand erledigen.
Das schöne daran ist, das ganze geht sogar aus einem anderen Plugin heraus, als dem eigentlichen Diagramm-Plugin. Damit entfallen alle genannten Nachteile des o.g. Ansatzes.
Wie geht es nun genau? Zuerst braucht man eine AdviceBinding (das ist eine Zuordnung eines Advices zu einem Meta-Modell-Element). Solche AdviceBindings kann man unterhalb des Meta-Element Eintrags im elementTypes-Extension Point anlegen.
<metamodel nsURI="http://sdq.ipd.uka.de/PalladioComponentModel/SEFF/1.0"> <adviceBinding class="de.uka.ipd.sdq.pcm.gmf.repository.edit.helpers.SeffEditHelperAdvice" id="de.uka.ipd.sdq.pcm.gmf.repository.ResourceDemandingSEFFAdvice" inheritance="all" typeId="de.uka.ipd.sdq.pcm.gmf.repository.ResourceDemandingSEFF_3102"> </adviceBinding> </metamodel>
Hier wird z.B. ein Advice registriert, der bei Ereignissen im Zusammenhang mit dem ResourceDemandingSeff (s. typeId) auftreten. Die typeId verweist dabei auf den entsprechenden Eintrag in der elementType Registy. In der Klasse SeffEditHelperAdvice kann man nun seine Advices durch Vererben der entsprechenden Vaterklassen-Methoden anbringen. SeffEditHelperAdvice muss dazu von AbstractEditHelperAdvice erben.
Damit der Advice auch berücksichtig wird, gilt es noch ein weiteres Konstrukt zu verstehen, das elementTypeBinding. Hier wird für ein bestimmtes Diagramm bestimmt, welche Teile der ElementTypeRegistry in diesem Diagramm berücksichtigt werden (ein Positiv-Filter über die TypeRegistry also). In den elementTypeBindings wird ein Advice aktiviert, indem er unterhalb des binding Elements des elementTypeBindings-Extension Points einfach benannt wird:
<binding context="PCM SEFF ModelClientContext"> <elementType ref="de.uka.ipd.sdq.pcm.gmf.repository.Repository_1100"/> .... <elementType ref="de.uka.ipd.sdq.pcm.gmf.repository.RequiredRole_4102"/> <advice ref="org.eclipse.gmf.runtime.diagram.core.advice.notationDepdendents"/> <advice ref="de.uka.ipd.sdq.pcm.gmf.repository.ResourceDemandingSEFFAdvice"> </advice> </binding>
Danach sollte der Advice aktiv sein und bei den überschriebenen Ereignissen in SeffEditHelperAdvice ausgeführt werden. Beispiele finden sich demnächst im Code der Palladio GMF Editoren.
Weitere Infos in Form der ursprünglichen Quelle finden sich auf der auch ansonsten interessanten Seite GMF Newsgroup FAQs
Dokumentation zur TypeRegistry und den hier verwendeten Extension Points und Begriffen findet sich im GMF Programmers Guide in der Eclipse GMF Hilfe.