The following example shows how to use the new feature persistence. A module List implements a list of elements with the properties name and number and a module Client offers the commands Init (to allocate the list), Insert (to insert elements in the list) and Print (to print the list). Each list has a header containing the property font, which determines the printing font.
MODULE List; IMPORT SYSTEM, Fonts, Persistent, PersMaps; TYPE List* = POINTER TO ListDesc; Elem* = POINTER TO ElemDesc; ListDesc* = RECORD first*: Elem; font*: Fonts.Font END; ElemDesc* = RECORD number*: LONGINT; name*: ARRAY 32 OF CHAR; next*: Elem END; PROCEDURE WriteMapper* (m: PersMaps.Map; o: SYSTEM.PTR); (* write mapper for List *) VAR l: List; BEGIN l := SYSTEM.VAL (List, o); (* o interpreted as of type List *) m.WriteObj (l.first); IF l.font # NIL THEN m.WriteString (l.font.name) ELSE m.WriteString ("") END END WriteMapper; PROCEDURE ReadMapper* (m: PersMaps.Map; o: SYSTEM.PTR); (* read mapper for List *) VAR l: List; str: ARRAY 32 OF CHAR; BEGIN l := SYSTEM.VAL (List, o); m.ReadObj (l.first); m.ReadString (str); IF str # "" THEN l.font := Fonts.This (str) ELSE l.font := Fonts.Default END END ReadMapper; BEGIN Persistent.RegisterType ("List.ListDesc", ReadMapper, WriteMapper) END List.
The objects of type Elem are mapped automatically, the objects of type List are mapped by the registered mappers WriteMapper and ReadMapper. Note that the procedures Insert and Print of the module Client access persistent data by ordinary pointer operations.
The separation in two modules List and Client shows that the mappers need not be defined in each client, but only in the module which defines the type. It can be seen that clients have just little work with persistence.
MODULE Client; IMPORT Fonts, Oberon, Persistent, Texts, In, Out, List; VAR w: Texts.Writer; PROCEDURE Init*; (* allocates a list, named root. The list header contains the property font *) VAR l: List.List; root, font: ARRAY 32 OF CHAR; BEGIN In.Open; In.Name (root); (* persistent list can be identified by a key: root*) In.Name (font); (* printing font *) NEW (l); l.first := NIL; l.font := Fonts.This (font); Persistent.SetRoot (l, root); IF Persistent.res = Persistent.alreadyExists THEN Out.String ("This root already exists") END END Init; PROCEDURE Insert*; (* inserts the couples (name nr) into the list named root *) VAR l: List.List; e: List.Elem; nr: LONGINT; name, root: ARRAY 32 OF CHAR; BEGIN In.Open; In.Name (root); (* persistent list can be identified by a key: root *) Persistent.GetRoot (l, root); IF l # NIL THEN In.Name (name); In.LongInt (nr); WHILE In.Done DO NEW (e); COPY (name, e.name); e.number := nr; e.next := l.first; l.first := e; In.Name (name); In.LongInt (nr) END END END Insert; PROCEDURE Print*; (* prints the list with the name root *) VAR l: List.List; e: List.Elem; root: ARRAY 32 OF CHAR; BEGIN In.Open; In.Name (root); Persistent.GetRoot (l, root); IF l # NIL THEN e := l.first; Texts.SetFont (w, l.font); WHILE e # NIL DO Texts.WriteString (w, e.name); Texts.WriteInt (w, e.number, 10); Texts.WriteLn (w); e := e.next END; Texts.Append (Oberon.Log, w.buf) END END Print; BEGIN Texts.OpenWriter (w) END Client.