“Savable” wird das neue “SaveCookie”

Bisher wurden in NetBeans alle geänderten Daten über eine Implementierung von SaveCookie im Lookup markiert. Wurden die Daten verändert, kam der Keks ins Lookup, nach dem Speichern wurde es dort wieder heraus geholt. Die Plattform sorgte dann dafür, dass die SaveAll Action aktiviert wurde und beim Programmende gab es einen netten Dialog, in dem noch einmal alle ungespeicherten DataObjects angezeigt wurden.

Jetzt gab es mit diesem Dialog zwei Probleme.

  1. Der Dialog hat “nur” alle DataObjects in der aktuellen Instanz der Anwendung geprüft ob sie einen SaveCookie in ihrem Lookup haben. Im Normalfall kein Problem, da den TopComponents ein DataObject zugewiesen bekommen welches sie darstellen. Möchte man jedoch die TCs nutzen ohne ein DataObject werden diese möglichen Änderungen im Dialog am Programmende nicht erfasst.
  2. Es gibt keine einfache Möglichkeit den angezeigten Text in diesem Dialog zu ändern. Es wird immer der diaplayName des DataObject verwendet. Bei den meisten IDE Projekten ist auch dies kein Problem, ist z.B. unter Java der Dateiname mit dem Klassennamen gekoppelt und somit ziemlich aussagekräftig. Aber schon wenn von einem Projekt trunk und branch gleichzeitig geöffnet und bearbeitet wird steigt die Ungewissheit welche Datei dort denn nun tatsächlich ungespeicherte Daten enthält.

Im Fehlereintrag #77210 wurde das erstgenannte Problem bereits 2006 für NetBeans 5.0 angesprochen. Es folgte eine angeregte Diskussion und auch eine ganze Weile Funkstille, aber es wurde nie wirklich vergessen. Ende letzten Jahres gab es dann noch einmal den entscheidenden Anstoß, wieder gefolgt von angeregter Diskussion, die dann in einem neuen Interface endete: Savable

Zusammen mit der Klasse AbstractSavable bilden sie einen Ersatz für das etwas angestaubte SaveCookie und soll ab NetBeans 7.1 verfügbar sein. Nicht nur, dass dieses den alten Namen “Cookie” beinhaltet, lassen sich damit die beiden oben genannten Probleme nicht wirklich lösen. Allerdings funktioniert das Savable etwas anders als der alte Keks.

Der erste, große Unterschied ist sicherlich, dass Savable nicht mehr im Lookup des DataObject untergebracht werden muss. Überhaupt ist die Implementierung losgelöst von der DataSystem API. Statt dessen gibt es in Savable ein öffentliches Lookup namens REGISTRY. Dieses globale Lookup nimmt alle Savable Implementierungen auf, die auf geänderte Daten verweisen. Es liegt dann an der Implementierung, ob auf die DataSystem API zugegriffen wird oder nicht. Auch ist es egal, ob ein DataObject verändert wurde oder eine TopComponent ungespeicherte Informationen bereit hält.

Mittels der beiden Methoden register() und unregister() kann man seine eigene Savable Implementierung in diese globale Sammelstelle hinein oder wieder herausholen. Entscheidend ist, dass eine Implementierung immer nur genau ein Mal in der REGISTRY vorhanden sein kann. Maßgeblich ist das Ergebnis der Methode equals in seinem Savable. Es wird dringend empfohlen diese Methode bei sich zu implementieren. Bei der Nutzung von AbstractSavable wird man dazu sogar gezwungen, da dort die Methoden als abstract angegeben sind. Allerdings gibt es in den JavaDocs zu AbstractSavable bereits eine Beispielimplementierung für diese Methoden, so dass diese Aufgabe meisten auf eine Copy&Paste Aktion hinaus läuft.

Wo wir schon beim AbstractSavable sind. Diese Standardimplementierung von Savable kann einem einiges an Arbeit ersparen und ein Blick in den Quellcode zeigt auch gleich, wie das zweite Problem von oben gelöst wurde. AbstractSavable hat eine abstrakte Methode findDisplayName welche einfach von der final gemachten Methode toString aufgerufen wird. Wie Jara Tulach auch in einem Kommentar zum Bugeintrag schreibt, recht es in einem eigenen Saveable also aus einfach diese Methode zu überschreiben und mit einem sinnvollen Rückgabewert zu versehen.

Zudem ist in AbstractSavable die Methoden save() aus dem Interface bereits implementiert und als final markiert. Die eigene Implementierung zum Speichern muss in er Methoden handleSave() erfolgen. Dafür ruft save automatisch die Methode unregister auf so dass man dies nicht mehr selber machen muss ((oder vergessen kann)).

Als besonderes Extra haben sich die Jungs von NetBeans auch noch gedacht, dass man das Icon im Exit-Dialog auch noch verändern kann. Dazu muss man nur das Interface javax.swing.Icon in seinem AbstractSavable implementieren. Aber auch hier gibt die JavaDoc bereits hinweise darauch, wie man dies elegant lösen kann.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyIconSavable extends AbstractSavable implements Icon {
  private Icon icon;
  private static final String ICON_BASE = "path/to/icon.png";

  public int getIconWidth(){
    initIcon();
    return icon.getIconWidth();
  }
  public int getIconHeight(){
    initIcon();
    return icon.getIconHeight();
  }
  public void paintIcon(Component c, Graphics g, int x, int y){
    initIcon();
    icon.paintIcon(c, g, x, y);
  }
  private void initIcon(){
    if(this.icon == null){
      icon = ImageUtilities.loadImageIcon(ICON_BASE, true);
    }
  }
  // other AbstractSavable stuff
}

Alles in allem denke ich, dass das neue System gut funktionieren wird und auch seinen Nutzen haben wird. Es sollte auch kein größeres Problem darstellen die alten SaveCookie Implementierungen auf Savable umzustellen, da das neue Interface jetzt auch gleichzeitig als Elterninterface für SaveCookie dient. Geändert werden muss lediglich, dass der Keks nicht mehr ins Lookup des DataObjects gehört sonder per Savable.REGISTRY.register() bekannt gemacht werden muss und mit Savable.REGISTRY.unregister() wieder entfernt wird.

Ein Gedanke zu ““Savable” wird das neue “SaveCookie”

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


× 9 = vierzig fünf

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>