home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / handson / archive / Issue156 / delphi / BookStream2 / bs2.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-06-09  |  19.0 KB  |  686 lines

  1. unit bs2;
  2. {$DEFINE DEBUG}
  3.  
  4. { PC Plus sample Delphi program.
  5.   Illustrates the basic techniques for declaring a class and
  6.   constructing and destroying objects.
  7.   Also shows how to create a simple object hierarchy including
  8.   list-owning objects, which can be saved to and loaded from disk
  9. }
  10.  
  11. interface
  12.  
  13. uses
  14.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  15.   Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls;
  16.  
  17.  
  18. type
  19.   TForm1 = class(TForm)
  20.     Panel1: TPanel;
  21.     Label1: TLabel;
  22.     Label2: TLabel;
  23.     ExtraLabel: TLabel;
  24.     BookEd: TEdit;
  25.     AuthorEd: TEdit;
  26.     ComboBox1: TComboBox;
  27.     ExtraEdit: TEdit;
  28.     NumberCombo: TComboBox;
  29.     AddBtn: TButton;
  30.     ClearBtn: TButton;
  31.     ExitBtn: TButton;
  32.     ShowBtn: TButton;
  33.     SaveBtn: TButton;
  34.     LoadBtn: TButton;
  35.     Workspace: TRichEdit;
  36.     Button1: TButton;
  37.     procedure ExitBtnClick(Sender: TObject);
  38.     procedure FormCreate(Sender: TObject);
  39.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  40.     procedure AddBtnClick(Sender: TObject);
  41.     procedure ShowBtnClick(Sender: TObject);
  42.     procedure ClearBtnClick(Sender: TObject);
  43.     procedure ComboBox1Change(Sender: TObject);
  44.     procedure SaveBtnClick(Sender: TObject);
  45.     procedure LoadBtnClick(Sender: TObject);
  46.     procedure Button1Click(Sender: TObject);
  47.   private
  48.     { Private declarations }
  49.   public
  50.     { --- My declarations --- }
  51.     procedure AddBookOb;
  52.     procedure AddHorrorBookOb;
  53.     procedure AddRefBookOb;
  54.     procedure AddSeriesBookOb;
  55.     procedure ShowObList;
  56.   end;
  57.  
  58. { declare a fixed-length string                 }
  59.   bkstr = string[255];
  60.   cnstr = string[15]; // to store the class name
  61. { declare a gorerating for HorrorBookOb         }
  62.   gorerating = 0..10;
  63.  
  64.   { BookObList is a special BookOb-managing TList }
  65.   BookObList = class(TList)
  66.     constructor Create;
  67.     destructor Destroy; override;
  68.     procedure FreeObs;
  69.     procedure SaveObList( fname : string );
  70.     procedure LoadObList( fname: string);
  71.     procedure WriteToStream( fs : TFileStream );
  72.     procedure ReadFromStream( fs: TFileStream);
  73.   end;
  74.  
  75. { declare a BookOb class        }
  76.   BookOb = class(TObject)
  77.     name   : bkstr;
  78.     author : bkstr;
  79.     cn     : cnstr; // name of the class
  80.     constructor Create( aName, anAuthor : bkstr );
  81.     constructor CreateFromStream( cname : cnstr; fs : TFileStream );
  82.     destructor Destroy; override;
  83.     function Describe : bkstr;  virtual;
  84.     procedure WriteToStream( fs : TFileStream ); virtual;
  85.     procedure ReadFromStream( fs: TFileStream); virtual;
  86.   end;
  87.  
  88. { declare a HorrorBookOb class }
  89.   HorrorBookOb = class(BookOb)
  90.     gorescore : integer;
  91.     constructor Create( aName, anAuthor : bkstr; aGorescore : gorerating );
  92.     destructor Destroy; override;
  93.     function Describe : bkstr; override;
  94.     procedure WriteToStream( fs : TFileStream ); override;
  95.     procedure ReadFromStream( fs: TFileStream); override;
  96.   end;
  97.  
  98. { declare a RefBookOb class }
  99.   RefBookOb = class(BookOb)
  100.     reftype : bkstr;
  101.     constructor Create( aName, anAuthor, aReftype : bkstr);
  102.     destructor Destroy; override;
  103.     function Describe : bkstr; override;
  104.     procedure WriteToStream( fs : TFileStream );  override;
  105.     procedure ReadFromStream( fs: TFileStream);   override;
  106.   end;
  107.  
  108.   {XXX New SeriesBookOb class XXX}
  109.   { This maintains a list of books, called Volumes, of the BookObList class }
  110.  SeriesBookOb = class(BookOb)
  111.     volumes : BookObList; { the volumes in this series }
  112.     constructor Create( aName, anAuthor : bkstr );
  113.     destructor Destroy; override;
  114.     function Describe : bkstr; override;
  115.     procedure AddVolumes( name, author : bkstr; vols: integer );
  116.     procedure WriteToStream( fs : TFileStream );  override;
  117.     procedure ReadFromStream( fs: TFileStream);   override;
  118.  
  119.   end;
  120.  
  121.  
  122. const
  123.   SAVEFILE = 'Library.sav';
  124. var
  125.   Form1: TForm1;
  126.   ObList : BookObList; { declare a TList to hold our objects }
  127.  
  128. implementation
  129.  
  130. {$R *.DFM}
  131.  
  132. // ===========================================
  133. // TFORM1 Start...
  134. // ===========================================
  135.  
  136. procedure TForm1.ShowObList;
  137. var
  138.    i :integer;
  139. begin
  140.   if ObList.Count = 0 then
  141.     Workspace.Lines.Add( 'No books in the list!' )
  142.   else
  143.   for i := 0 to ObList.Count - 1 do
  144.       Workspace.Lines.Add( Format('>> BOOK %d: %s',
  145.                            [i,BookOb(ObList.Items[i]).Describe]) );
  146. end;
  147.  
  148. procedure TForm1.Button1Click(Sender: TObject);
  149. begin
  150.   WorkSpace.Clear;
  151. end;
  152.  
  153. procedure TForm1.SaveBtnClick(Sender: TObject);
  154. begin
  155.   ObList.SaveObList( SAVEFILE );
  156.   Workspace.Lines.Add( 'Saved' );
  157. end;
  158.  
  159. procedure TForm1.LoadBtnClick(Sender: TObject);
  160. begin
  161.   if not FileExists( SAVEFILE ) then
  162.      ShowMessage( SAVEFILE + ' not found!' )
  163.   else
  164.   begin
  165.     ObList.LoadObList( SAVEFILE );
  166.     ShowObList;
  167.   end;
  168. end;
  169.  
  170. procedure TForm1.ExitBtnClick(Sender: TObject);
  171. begin
  172.   Close;
  173. end;
  174.  
  175. procedure TForm1.FormCreate(Sender: TObject);
  176. begin
  177.   { Create a BookObList called ObList when the main form is created }
  178.   ObList := BookObList.Create;
  179. end;
  180.  
  181. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  182. begin
  183.   ObList.FreeObs;     { Free the objects in the ObList, then...      }
  184.   ObList.Free; { Free the ObList when the main form is closed }
  185. end;
  186.  
  187. { ----- add specific types of objects ----- }
  188. procedure TForm1.AddBookOb;
  189. { add an ordinary BookOb to the ObList and display its data on the List box }
  190. var
  191.   book : BookOb;
  192. begin
  193.   book := BookOb.Create( BookEd.Text, AuthorEd.Text );
  194.   ObList.Add( book );
  195.   Workspace.Lines.Add( 'Book added' );
  196. end;
  197.  
  198. procedure TForm1.AddRefBookOb;
  199. { add a RefBookOb to the ObList and display its data on the List box        }
  200. var
  201.   book : RefBookOb;
  202. begin
  203.   if (ExtraEdit.Text = '') then
  204.         MessageDlg('You must enter a book type (e.g. "Physics" or "Art") ',
  205.                     mtInformation, [mbOk], 0)
  206.   else
  207.   begin
  208.     book := RefBookOb.Create( BookEd.Text, AuthorEd.Text, ExtraEdit.Text );
  209.     ObList.Add( book );
  210.     Workspace.Lines.Add( 'Reference Book added' );
  211.   end;
  212. end;
  213.  
  214. procedure TForm1.AddHorrorBookOb;
  215. { add a HorrorBookOb to the ObList and display its data on the List box     }
  216. var
  217.   book  : HorrorBookOb;
  218.   score : integer;
  219. begin
  220.   score := StrToIntDef( NumberCombo.Text, -1 );//XXX Use StrToIntDef and you
  221.   if score = -1 then                           //won't need to handle exceptions
  222.      MessageDlg('Invalid gorescore entry!', mtInformation,[mbOk], 0)
  223.   else
  224.   if (score < 0) or (score > 10) then
  225.         MessageDlg('The score must be a number from 0 to 10!', mtInformation,
  226.                    [mbOk], 0)
  227.   else
  228.   begin                     { if all is ok, create the object       }
  229.     book := HorrorBookOb.Create( BookEd.Text, AuthorEd.Text, score );
  230.     ObList.Add( book );
  231.     Workspace.Lines.Add( 'Horror Book added' );
  232.   end;
  233. end;
  234.  
  235. procedure TForm1.AddSeriesBookOb;
  236. { add a SeriesBookOb to the ObList and display its data on the List box
  237.   For simplicity (and ease of displaying data!), a series is here restricted
  238.   from 2 to 4 volumes. This is an arbitrary restriction and it can easily
  239.   be removed or extended }
  240. var
  241.   book  : SeriesBookOb;
  242.   vols : integer;
  243. begin
  244.   vols := StrToIntDef( NumberCombo.Text, -1 );
  245.   if vols = -1 then
  246.      MessageDlg('Invalid number of volumes!', mtInformation,[mbOk], 0)
  247.   else
  248.   if (vols < 2) or (vols > 4 ) then
  249.         MessageDlg('A series must have from 2 to 4 volumes!', mtInformation,
  250.                    [mbOk], 0)
  251.   else
  252.   begin
  253.     book := SeriesBookOb.Create( BookEd.Text, AuthorEd.Text );
  254. { XXX Just for testing, add some volumes. You could write an interactive
  255.   procedure here to let the user add volumes with different authors or even of
  256.   different book types }
  257.     book.AddVolumes( BookEd.Text, AuthorEd.Text, vols );
  258.     ObList.Add( book );
  259.     {$IFDEF DEBUG}
  260.     Caption := 'Adding book of class: ' + book.ClassName;
  261.     {$ENDIF }
  262.     Workspace.Lines.Add( 'Series Book added' );
  263.    end;
  264. end;
  265.  
  266. procedure TForm1.AddBtnClick(Sender: TObject);
  267. { User wants to add a book object. Determine the type of object         }
  268. { by the Combo box item that's been selected, then call an appropriate  }
  269. { method.                                                               }
  270. begin
  271.   if ((BookEd.Text = '') or (AuthorEd.Text = '')) then
  272.         MessageDlg('You must enter a book and an author!', mtInformation,
  273.       [mbOk], 0)
  274.   else
  275.   if ComboBox1.Text = 'Horror' then
  276.     AddHorrorBookOb
  277.   else
  278.   if ComboBox1.Text = 'Reference' then
  279.     AddRefBookOb
  280.   else
  281.   if ComboBox1.Text = 'Series' then
  282.     AddSeriesBookOb
  283.  else
  284.     AddBookOb;
  285. end;
  286.  
  287. procedure TForm1.ShowBtnClick(Sender: TObject);
  288. begin
  289.   ShowObList;
  290. end;
  291.  
  292. procedure TForm1.ClearBtnClick(Sender: TObject);
  293. begin
  294.   ObList.FreeObs;
  295.   Workspace.Lines.Add('OK');
  296. end;
  297.  
  298.  
  299. procedure TForm1.ComboBox1Change(Sender: TObject);
  300. { When a change (normally a selection) is made in the ComboBox, see if the    }
  301. { selected item indicates a special type of book. If so, display a label      }
  302. { and text entry field to allow the user to enter the additional data needed  }
  303. { to create an object of the specified type.                                  }
  304. begin
  305.   if ComboBox1.Text = 'Horror' then
  306.   begin
  307.     ExtraLabel.Caption := 'Enter Gore score [1 to 10]';
  308.     ExtraLabel.Show;
  309.     ExtraEdit.Hide;
  310.     NumberCombo.Show;
  311.     NumberCombo.SetFocus;
  312.   end
  313.   else if ComboBox1.Text = 'Reference' then
  314.   begin
  315.     ExtraLabel.Caption := 'Enter the type of reference book';
  316.     ExtraEdit.Text := 'General';
  317.     ExtraLabel.Show;
  318.     ExtraEdit.Show;
  319.     NumberCombo.Hide;
  320.     ExtraEdit.SetFocus;
  321.   end
  322.   else if ComboBox1.Text = 'Series' then
  323.   begin
  324.     ExtraLabel.Caption := 'Enter number of volumes in series';
  325.     ExtraLabel.Show;
  326.     ExtraEdit.Hide;
  327.     NumberCombo.Show;
  328.   end
  329.   else
  330.   begin   { if it's an ordinary book, don't display the extra edit box       }
  331.     ExtraLabel.Hide;
  332.     ExtraEdit.Hide;
  333.     NumberCombo.Hide;
  334.   end;
  335. end;
  336.  
  337.  
  338. // ===========================================
  339. // BOOKOBLIST Start...
  340. // ===========================================
  341. // constructor
  342. constructor BookObList.Create;
  343. begin
  344.   inherited Create;
  345. end;
  346.  
  347. // destructor
  348. destructor BookObList.Destroy;
  349. begin
  350.    FreeObs;
  351.    inherited Destroy;
  352. end;
  353.  
  354. procedure BookObList.SaveObList( fname : string );
  355. { Create a file stream then ask BookObList to write itself to that stream }
  356. var
  357.    fs : TFileStream;
  358. begin
  359.    fs := TFileStream.Create(fname, fmCreate );
  360.    try
  361.      self.WriteToStream( fs );
  362.    finally;
  363.      fs.Free;
  364.    end;
  365. end;
  366.  
  367. procedure BookObList.LoadObList( fname : string );
  368. { Create a file stream then ask BookObList to read itself from that stream }
  369. var
  370.    fs : TFileStream;
  371. begin
  372.   fs := TFileStream.Create(fname, fmOpenRead );
  373.    self.Clear;
  374.    try
  375.      self.ReadFromStream(fs);
  376.    finally
  377.      fs.Free;
  378.    end;
  379. end;
  380.  
  381. procedure BookObList.FreeObs;
  382. { Free the objects in the ObList  }
  383. var
  384.   i : integer;
  385. begin
  386.   for i := 0 to self.Count - 1 do
  387.       if self.Items[i] <> nil then
  388.       begin
  389. {$IFDEF DEBUG}
  390.          Form1.Workspace.Lines.Add( 'Call to FREE Ob: ' +
  391.                              BookOb(self.Items[i]).Describe );
  392. {$ENDIF}
  393.          BookOb(self.Items[i]).Free; // each BookOb knows how to Free itself
  394.       end;
  395.   self.Clear;
  396. end;
  397.  
  398. //--- BookObList Streams ---
  399. procedure BookObList.ReadFromStream(fs: TFileStream);
  400. var
  401.    cn : cnstr;
  402.    i, obcount : integer;
  403. begin
  404. {$IFDEF DEBUG}
  405.    Form1.Workspace.Lines.Add( 'BookObList.ReadFromStream' );
  406. {$ENDIF}
  407.     { First read a count of objects }
  408.    fs.ReadBuffer(obcount, sizeof(obcount));
  409. {$IFDEF DEBUG}
  410.           Form1.Workspace.Lines.Add( 'BookObList.ReadFromStream, obcount = '+ IntToStr(obcount) );
  411. {$ENDIF}
  412.    for i := 0 to obcount - 1 do
  413.    begin     { read classname and call appropriate constructor }
  414.        fs.ReadBuffer(cn, sizeof(cn));
  415. {$IFDEF DEBUG}
  416.         Form1.Workspace.Lines.Add('ClassName read in is: ' + cn );
  417. {$ENDIF}
  418.        if cn = 'HorrorBookOb' then
  419.           self.Add(HorrorBookOb.CreateFromStream(cn,fs))
  420.        else if cn = 'RefBookOb' then
  421.           self.Add(RefBookOb.CreateFromStream(cn,fs))
  422.        else if cn = 'SeriesBookOb' then
  423.        begin
  424. {$IFDEF DEBUG}
  425.           Form1.Workspace.Lines.Add( 'SeriesBookOb read from Stream!' ) ;
  426. {$ENDIF}
  427.           self.Add(SeriesBookOb.CreateFromStream(cn,fs));
  428.        end
  429.       else
  430.           self.Add(BookOb.CreateFromStream(cn,fs));
  431.    end;
  432. end;
  433.  
  434. procedure BookObList.WriteToStream(fs: TFileStream);
  435. var
  436.     i : integer;
  437. begin
  438.      fs.WriteBuffer( self.Count, sizeof(self.Count ) );
  439.      for i := 0 to self.Count - 1 do
  440.          BookOb(self[i]).WriteToStream( fs ); { call VIRTUAL method }
  441. end;
  442.  
  443. // ...BOOKOBLIST End
  444.  
  445.  
  446.  
  447. // ===========================================
  448. // BOOKOB FAMILY TREE Start...
  449. // ===========================================
  450.  
  451. // BOOKOB
  452.  
  453. constructor BookOb.Create( aName, anAuthor : bkstr );
  454. { a standard BookOb. Call default initialisation, then init the 2 fields }
  455. begin
  456.   inherited Create;
  457.   cn   := self.className; //XXX this is the class name of *actual* bookob type
  458. {$IFDEF DEBUG}
  459.   Form1.Workspace.Lines.Add( Format('CONSTRUCTOR: BookOb.Create: %s',
  460.                              [ cn ]) );
  461. {$ENDIF}
  462.   name := aName;
  463.   author := anAuthor;
  464. end;
  465.  
  466. // alternative constructor.
  467. // creates itself using data read from stream
  468. constructor BookOb.CreateFromStream( cname : cnstr; fs: TFileStream);
  469. begin
  470.    inherited Create;
  471. {$IFDEF DEBUG}
  472.   Form1.Workspace.Lines.Add( Format('CONSTRUCTOR: BookOb.CreateFromStream: %s',
  473.                              [ cname ]) );
  474. {$ENDIF}
  475.    cn := cname;
  476.    ReadFromStream( fs );
  477. end;
  478.  
  479. destructor BookOb.Destroy;
  480. begin
  481. {$IFDEF DEBUG}
  482.   Form1.Workspace.Lines.Add( 'DESTRUCTOR: BookOb.Destroy' );
  483. {$ENDIF}
  484.   name := '';
  485.   author := '';
  486.   cn := '';
  487.   inherited Destroy;
  488. end;
  489.  
  490. procedure BookOb.WriteToStream( fs : TFileStream );
  491. begin
  492.   fs.WriteBuffer(cn, sizeof(cn));
  493.   fs.WriteBuffer(author, sizeof(author) );
  494.   fs.WriteBuffer(name, sizeof(name ) );
  495. {$IFDEF DEBUG}
  496. Form1.Workspace.Lines.Add(Format('BookOb.WriteToStream. %s by %s, class= %s',
  497.                                   [name,author,cn] ));
  498. {$ENDIF}
  499. end;
  500.  
  501. procedure BookOb.ReadFromStream( fs : TFileStream );
  502. begin
  503.   fs.ReadBuffer(author, sizeof(author) );
  504.   fs.ReadBuffer(name, sizeof(name ) );
  505. {$IFDEF DEBUG}
  506.   Form1.Workspace.Lines.Add( Format(
  507.        'In BookOb.ReadFromStream, Book author=%s,name=%s.', [author,name] ));
  508. {$ENDIF}
  509. end;
  510.  
  511. function BookOb.Describe : bkstr;
  512. begin
  513.   result := Format( '[General Book] %s by %s', [name,author]);
  514. end;
  515.  
  516. // HORRORBOOKOB
  517. constructor HorrorBookOb.Create( aName, anAuthor : bkstr; aGorescore : gorerating );
  518. { a HorrorBookOb. Call its ancestor (BookOb) constructor, then init gorescore }
  519. begin
  520.   inherited Create(aName, anAuthor);
  521. {$IFDEF DEBUG}
  522.   Form1.Workspace.Lines.Add( 'CONSTRUCTOR: HorrorBookOb.Create' );
  523. {$ENDIF}
  524.   gorescore := aGorescore;
  525. end;
  526.  
  527. destructor HorrorBookOb.Destroy;
  528. begin
  529. {$IFDEF DEBUG}
  530.   Form1.Workspace.Lines.Add( 'DESTRUCTOR: HorrorBookOb.Destroy' );
  531. {$ENDIF}
  532.   gorescore := 0;
  533.   inherited Destroy;
  534. end;
  535.  
  536. procedure HorrorBookOb.WriteToStream( fs : TFileStream );
  537. begin
  538.   fs.WriteBuffer(cn, sizeof(cn));
  539.   fs.WriteBuffer(author, sizeof(author) );
  540.   fs.WriteBuffer(name, sizeof(name ) );
  541.   fs.WriteBuffer(gorescore, sizeof(gorescore ) );
  542. {$IFDEF DEBUG}
  543. Form1.Workspace.Lines.Add(Format('HorrorBookOb.WriteToStream. %s by %s, class= %s',
  544.                                   [name,author,cn] ));
  545. {$ENDIF}
  546.  
  547. end;
  548.  
  549. procedure HorrorBookOb.ReadFromStream( fs : TFileStream );
  550. begin
  551.   fs.ReadBuffer(author, sizeof(author) );
  552.   fs.ReadBuffer(name, sizeof(name ) );
  553.   fs.ReadBuffer(gorescore, sizeof(gorescore ) );
  554. end;
  555.  
  556. function HorrorBookOb.Describe : bkstr;
  557. begin
  558.   result := Format( '[Horror Book] %s by %s. Gore Score: %d',
  559.                     [name,author,gorescore]);
  560. end;
  561.  
  562. // REFBOOKOB
  563. constructor RefBookOb.Create( aName, anAuthor, aReftype : bkstr );
  564. { a RefBookOb. Call its ancestor (BookOb) constructor, then init reftype      }
  565. begin
  566.   inherited Create(aName, anAuthor);
  567. {$IFDEF DEBUG}
  568.   Form1.Workspace.Lines.Add( 'CONSTRUCTOR: RefBookOb.Create' );
  569. {$ENDIF}
  570.   reftype := aReftype;
  571. end;
  572.  
  573. destructor RefBookOb.Destroy;
  574. begin
  575. {$IFDEF DEBUG}
  576.   Form1.Workspace.Lines.Add( 'DESTRUCTOR: RefBookOb.Destroy' );
  577. {$ENDIF}
  578.   reftype := '';
  579.   inherited Destroy;
  580. end;
  581.  
  582. procedure RefBookOb.WriteToStream( fs : TFileStream );
  583. begin
  584.   fs.WriteBuffer(cn, sizeof(cn));
  585.   fs.WriteBuffer(author, sizeof(author) );
  586.   fs.WriteBuffer(name, sizeof(name ) );
  587.   fs.WriteBuffer(reftype, sizeof(reftype ) );
  588. {$IFDEF DEBUG}
  589. Form1.Workspace.Lines.Add(Format('RefBookOb.WriteToStream. %s by %s, class= %s',
  590.                                   [name,author,cn] ));
  591. {$ENDIF}
  592.  
  593. end;
  594.  
  595. procedure RefBookOb.ReadFromStream( fs : TFileStream );
  596. begin
  597.   fs.ReadBuffer(author, sizeof(author) );
  598.   fs.ReadBuffer(name, sizeof(name ) );
  599.   fs.ReadBuffer(reftype, sizeof(reftype ) );
  600. end;
  601.  
  602. function RefBookOb.Describe : bkstr;
  603. begin
  604. result := Format( '[Reference Book] %s by %s. Book type: %s',
  605.                     [name,author,reftype]);
  606. end;
  607.  
  608.  
  609. // SERIESBOOKOB
  610. constructor SeriesBookOb.Create(aName, anAuthor: bkstr);
  611. begin
  612.   inherited Create(aName, anAuthor);
  613. {$IFDEF DEBUG}
  614.   Form1.Workspace.Lines.Add( 'CONSTRUCTOR: SeriesBookOb.Create' );
  615. {$ENDIF}
  616.   Volumes := BookObList.Create;
  617. end;
  618.  
  619. destructor SeriesBookOb.Destroy;
  620. begin
  621.   Volumes.FreeObs;
  622.   Volumes.Free;
  623.   inherited Destroy;
  624. end;
  625.  
  626. procedure SeriesBookOb.AddVolumes(name, author: bkstr; vols : integer );
  627. // simply initialise vols number of volumes owned by SeriesBookOb
  628. var
  629.   i    : integer;
  630.   book : BookOb;
  631. begin
  632.   for i := 1 to vols do
  633.   begin
  634.     book := BookOb.Create( 'Vol: [' + IntToStr(i) + ']' , author );
  635.     Volumes.Add( book );
  636.   end;
  637. end;
  638.  
  639. procedure SeriesBookOb.WriteToStream(fs: TFileStream);
  640. begin
  641.   fs.WriteBuffer(cn, sizeof(cn));
  642.   fs.WriteBuffer(author, sizeof(author) );
  643.   fs.WriteBuffer(name, sizeof(name ) );
  644. {$IFDEF DEBUG}
  645. Form1.Workspace.Lines.Add(Format('SeriesBookOb.WriteToStream. %s by %s, class= %s',
  646.                                   [name,author,cn] ));
  647. {$ENDIF}
  648.   // write Volumes of BookOb objects
  649.   Volumes.WriteToStream( fs );
  650. end;
  651.  
  652.  
  653.  
  654. procedure SeriesBookOb.ReadFromStream(fs: TFileStream);
  655. begin
  656.   fs.ReadBuffer(author, sizeof(author) );
  657.   fs.ReadBuffer(name, sizeof(name ) );
  658. {$IFDEF DEBUG}
  659.   Form1.Workspace.Lines.Add( Format('SeriesBook author=%s,name=%s.', [author,name] ));
  660. {$ENDIF}
  661.   // read Volumes of BookOb objects
  662.   Volumes := BookObList.Create; // first, remember to Create Volumes
  663.   Volumes.ReadFromStream( fs );
  664. end;
  665.  
  666.  
  667. function SeriesBookOb.Describe: bkstr;
  668. var
  669.    vols : string;
  670.    i    : integer;
  671. begin
  672.    vols := 'Volumes = ';
  673.    for i := 0 to Volumes.Count - 1 do
  674.        vols := vols + BookOb(Volumes[i]).Describe+'. ';
  675.    result := Format( '[Series of %d volumes] %s by %s. %s',
  676.                     [i, name,author,vols]);
  677. end;
  678.  
  679. // ...BOOKOB FAMILY TREE End
  680.  
  681. end.
  682.  
  683.  
  684.  
  685.  
  686.