Austausch der Figure eines EditParts zur Laufzeit in GEF


Aus Wirthi's Wiki

Wechseln zu: Navigation, Suche

Folgender Artikel beschreibt ein Problem und dessen Lösung, das sich ergibt, wenn man in GEF die Figure eines EditParts zur Laufzeit austauschen will.

[bearbeiten] Vorgeschichte

Bei der Progammierung mit dem Graphical Editor Framework (GEF) von Eclipse benutzt man eine Model-View-Controller-Architektur. Im Normalfall hat man für jedes Modelelement (genau) ein Controller-Element (einen "EditPart") und jeder EditPart hat (genau) eine View ("Figure"). Wenn sich Daten im Model ändern, werden die betroffenen EditParts informiert, um die aktualisierten Daten darzustellen - entweder auf der eigenen Figure oder durch hinzufügen oder entfernen neuer Kindelemente.

Dieser Ablauf wird durch GEF sehr gut unterstützt. Nicht möglich scheint hingegen der Anwendungsfall, dass sich ein Model-Element inhaltlich nur geringfügig ändert (etwa eine boolean-Variable gesetzt wird), und sich dadurch die Anzeige komplett ändert. Wenn das Model-Element nämlich das gleiche bleibt, ändert sich auch der betroffene EditPart - und damit seine Figure - nicht.

In meinem Anwendungsfall hatte ich einen lauffähigen Editor, der um eine zweite Hierarchie an Figures erweitert werden sollte. Alle Elemente sollten zur Laufzeit von der einen in die andere Ansicht umgeschaltet werden können. Mein Editor unterstützte dies durch entsprechende Interfaces. Problematisch waren allerding die EditPart; diese unterstützt das Ändern der Figure zur Laufzeit nicht. Ein EditPart behält seine Figure für immer. Will man die Figure durch eine neue ersetzen, muss man auch den EditPart löschen und neu anlegen. Dies war aber für meinen Anwendungsfall zu aufwändig. Ein direktes Wechseln der View im EditPart wäre zwar möglich - die Variable kann problemlos geändert werden. Dadurch wird diese aber nicht an der korrekten Stelle in der Parent-Figure eingefügt. Ich suchte und fand daher folgende Lösung:

[bearbeiten] Lösung

Nach etwas Graben im GEF-Sourcecode fand ich die Methode AbstractEditPart.refreshChildren. Diese stellt sicher, dass die Hierarchie an EditParts konsistent mit der der Model-Objekte wird. Es werden die existierenden Kind-EditParts mit denen verglichen, die laut Liste an Model-Kindelementen existieren sollten. Fehler werden dabei berichtigt, also überflüssige EditParts entfernt, neue angelegt und bestehende gegebenenfalls umsortiert. An dieser Stelle konnte ich meinen Code einfügen, der die Figure austauschte.

Obwohl die Methode als protected markiert ist und kein final-Attribut hat, ist im JavaDoc "This method should not be overridden" angegeben. Der Aufbau der Methode erlaubt auch nicht, die Methode zu überschreiben und die bisherige Funktionalität mittels super-Aufruf zu nutzen - mein Code sollte ja mitten in der Methode platziert werden. Es bliebt daher nur der (unschöne) Weg, den bestehenden Code zu kopieren und an der gewünschten Stelle anzupassen.

Relevant sind die Zeilen

	//Do a quick check to see if editPart[i] == model[i]
	if (i < children.size()
		&& ((EditPart) children.get(i)).getModel() == model)
			continue;

continue wird also aufgerufen, wenn ein passender EditPart gefunde wird, der sich bereits an der erwarteten Position befindet. Hier fügte ich meinen eigenen Code ein. Anstatt sofort continue; aufzurufen, prüfte ich, ob die Figure des EditParts (noch) die war, die er laut Model haben sollte. War das nicht der Fall, so löschte ich die alte Figure und fügte eine neue ein. Dies lässt sich etwa durch einen Aufruf von:

	removeChildVisual(childEditPart);
	addChildVisual(childEditPart, i);

bewerkstelligen. Da ich addChildVisual ohnehin in allen EditParts überschrieben hatte, konnte ich dort sicherstellen, dass nicht die alte, sondern die neue Figure eingefügt wird.

Zusätzlich musste ich aber noch die Variable children löschen, damit diese bei einem nachfolgenden Aufruf von refresh() ebenfalls aktualisiert werden. Dies war auch schwieriger als gedacht, da die Variable protected ist. Also musste ich eine neue Funktion clearChildren() in meine EditPart-Basisklasse einfügen, die nichts anderes tut, als die Variable children auf eine leere ArrayList zurück zu setzen.

[bearbeiten] Fazit

In meiner Situation war dieser Weg der einfachste. Die Lösung ist auch halbwegs sauber - einzig Kopfzerbrechten bereitet mir, warum die überschriebene Methode zwar als überschreibbar markiert war, das aber per JavaDoc verboten wurde.

Möglicherweise wäre der Austausch einfacher zu bewerkstelligen - ich fand allerdings leider keine gangbare Lösung. Für sachdienliche Hinweise bin ich immer dankbar.


Persönliche Werkzeuge