Die Betrachtung des Document
s soll mit einer Lösung zur Darstellung von Document
s und deren Seiten in einer grafischen Oberfläche
abgeschlossen werden.
Ein
Problem hierbei ist generell, dass zum Beispiel die Seitenliste Änderungen unterworfen sein
kann, die nicht auf dem EventDispatchThread (EDT)
von AWT/Swing oder SWT initiiert wurden. Die
Herausforderung besteht nun darin, stets eine stabile und konsistente Version der
Seitenliste für den EDT vorhalten zu müssen und diese Repräsentation nur
so zu aktualisieren, dass die Änderungen jeweils während eines EDT-Zyklus
für den EDT sichtbar werden. Um dies nicht wieder und wieder
implementieren zu müssen, bietet jadice 5 eine Fassade, die dies erledigt und einem
beliebigen Dokument vorangestellt werden kann: das UIDocument
beziehungsweise für
AWT/Swing das SwingUIDocument. Die Implementierung
stützt sich unter anderem auf die GlazedLists-Basisklasse
ThreadProxyEventList
beziehungsweise
SwingThreadProxyEventList
. Benutzt man ein solches UIDocument
zur »Versorgung« eines Objektes der
Benutzeroberfläche, so vereinfacht sich diese Aufgabe gravierend:
Alle Änderungen der Seitenliste werden immer im Rahmen eines EDT-Zyklus sichtbar.
Die Notwendigkeit zum Locking beziehungsweise zur Synchronisation entfällt für alle rein lesenden Zugriffe.
Auch die Benachrichtigung über Änderungen über die Listener erfolgt immer auf dem EDT.
Dadurch können Oberflächenelemente direkt als Reaktion auf diese Änderungen aktualisiert werden, ohne SwingUtilities.invokeLater(...) oder Ähnliches benutzen zu müssen.
Wie werden UIDocument
s nun konkret benutzt? Aus Performancegründen
ist es wünschenswert, zu einem gegebenen Document
generell immer nur ein einziges UIDocument
vorzuhalten, ganz gleich, wie viele
Oberflächenelemente bedient werden müssen. Wir haben deshalb den Zugriff auf das SwingUIDocument
in eine statische Getter-Methode
gekapselt: SwingUIDocument.get(myDocument). Code, der das UIDocument
verwendet, könnte zum Beispiel wie folgt
aussehen:
// zum Beispiel während der Initialisierung einer Applikation... myDocument = new BasicDocument(); // [...] // während der Initialisierung einer Swing-Oberflächenkomponente... myComponent.add(new JLabel("Anzahl Seiten: ")); // Seitenanzahl darstellen und aktuell halten final UIDocument<Component> uiDocument = SwingUIDocument.get(myDocument); final JLabel pageCountLabel = new JLabel( Integer.toString(uiDocument.getPageCount()) ); uiDocument.addDocumentListener(new DocumentAdapter() { @Override public void listChanged(ListEvent<Page> listEvent) { pageCountLabel.setText(Integer.toString(uiDocument.getPageCount())); } }); // [...] // Später irgendwann, auf einem beliebigen Thread... myDocument.addPage(somePage); myDocument.addPage(someOtherPage); // [...]