GMF Tutorial: 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 AbstractHelperAdvice 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.
Hinweise
Der XML-Abschnitt "clientContext" ist in der plugin.xml des Helpers (Advice deklarierend) nicht erforderlich.