Die Betrachtung des Documents soll mit einer Lösung zur Darstellung von Documents 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 UIDocuments 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); // [...]


