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

  1. unit bs;
  2. { PC Plus sample Delphi program.
  3.   Illustrates the basic techniques for declaring a class and
  4.   constructing and destroying objects.
  5.   Also shows how to create a simple object hierarchy.
  6. }
  7.  
  8. { Note specific BookOb descendants' CreateFromStream calls their ancestor
  9. BookOb CreateFromStream. This calls ReadFromStream (which is overridden so it
  10. calls the descendent's ReadFromStream! ) Trace execution to understand! }
  11.  
  12.  
  13. interface
  14.  
  15. uses
  16.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  17.   Forms, Dialogs, StdCtrls;
  18.  
  19.  
  20. type
  21.   TForm1 = class(TForm)
  22.     ListBox1: TListBox;
  23.     BookEd: TEdit;
  24.     AuthorEd: TEdit;
  25.     Label1: TLabel;
  26.     Label2: TLabel;
  27.     AddBtn: TButton;
  28.     ClearBtn: TButton;
  29.     ExitBtn: TButton;
  30.     ShowBtn: TButton;
  31.     ComboBox1: TComboBox;
  32.     ExtraLabel: TLabel;
  33.     ExtraEdit: TEdit;
  34.     GoreScoreCombo: TComboBox;
  35.     SaveBtn: TButton;
  36.     LoadBtn: 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.   private
  47.     { Private declarations }
  48.   public
  49.     { --- My declarations --- }
  50.     procedure AddBookOb;
  51.     procedure AddHorrorBookOb;
  52.     procedure AddRefBookOb;
  53.     procedure ShowObList;
  54.   end;
  55.  
  56. { declare a fixed-length string                 }
  57.   bkstr = string[70];
  58.   cnstr = string[15]; // to store the class name
  59. { declare a gorerating for HorrorBookOb         }
  60.   gorerating = 0..10;
  61.  
  62. { declare a BookOb class        }
  63.   BookOb = class(TObject)
  64.     name   : bkstr;
  65.     author : bkstr;
  66.     cn     : cnstr; // name of the class
  67.     constructor Create( aName, anAuthor : bkstr );
  68.     constructor CreateFromStream( cname : cnstr; fs : TFileStream );
  69.     destructor Destroy; override;
  70.     function Describe : bkstr;  virtual;
  71.     procedure WriteToStream( fs : TFileStream ); virtual; { XXX }
  72.     procedure ReadFromStream( fs: TFileStream); virtual; { XXX }
  73.   end;
  74.  
  75. { declare a HorrorBookOb class }
  76.   HorrorBookOb = class(BookOb)
  77.     gorescore : integer;
  78.     constructor Create( aName, anAuthor : bkstr; aGorescore : gorerating );
  79.     destructor Destroy; override;
  80.     function Describe : bkstr; override;
  81.     procedure WriteToStream( fs : TFileStream ); override; { XXX }
  82.     procedure ReadFromStream( fs: TFileStream); override; { XXX }
  83.   end;
  84.  
  85. { declare a RefBookOb class }
  86.   RefBookOb = class(BookOb)
  87.     reftype : bkstr;
  88.     constructor Create( aName, anAuthor, aReftype : bkstr);
  89.     destructor Destroy; override;
  90.     function Describe : bkstr; override;
  91.     procedure WriteToStream( fs : TFileStream );  override; { XXX }
  92.     procedure ReadFromStream( fs: TFileStream);   override; { XXX }
  93.   end;
  94.  
  95.   { BookObList is a special BookOb-managing TList }
  96.   BookObList = class(TList)
  97.     constructor Create;
  98.     destructor Destroy; override;
  99.     procedure FreeObs;
  100.     procedure SaveObList( fname : string );
  101.     procedure LoadObList( fname: string);
  102.   end;
  103.  
  104. const
  105.   SAVEFILE = 'Library.sav';
  106. var
  107.   Form1: TForm1;
  108.   ObList : BookObList; { declare a TList to hold our objects }
  109.  
  110. implementation
  111.  
  112. {$R *.DFM}
  113.  
  114. // ===========================================
  115. // TFORM1 Start...
  116. // ===========================================
  117.  
  118. procedure TForm1.ShowObList;
  119. { Show each ob's ID (its name and author) in the ListBox }
  120. { also show extra data for HorrorBookObs and RefBookObs  }
  121. var
  122.    i :integer;
  123. begin
  124. {  ListBox1.Clear; }
  125.   if ObList.Count = 0 then
  126.     ListBox1.Items.Add( 'No books in the list!' )
  127.   else
  128.   for i := 0 to ObList.Count - 1 do
  129.       ListBox1.Items.Add( BookOb(ObList.Items[i]).Describe );
  130. end;
  131.  
  132. procedure TForm1.SaveBtnClick(Sender: TObject);
  133. begin
  134.   ObList.SaveObList( SAVEFILE );
  135.   ListBox1.Items.Add( 'Saved' );
  136. end;
  137.  
  138. procedure TForm1.LoadBtnClick(Sender: TObject);
  139. begin
  140.   if not FileExists( SAVEFILE ) then
  141.      ShowMessage( SAVEFILE + ' not found!' )
  142.   else
  143.   begin
  144.     ObList.LoadObList( SAVEFILE );
  145.     ShowObList;
  146.   end;
  147. end;
  148.  
  149. procedure TForm1.ExitBtnClick(Sender: TObject);
  150. begin
  151.   Close;
  152. end;
  153.  
  154. procedure TForm1.FormCreate(Sender: TObject);
  155. begin
  156.   { Create a BookObList called ObList when the main form is created }
  157.   ObList := BookObList.Create;
  158. end;
  159.  
  160. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  161. begin
  162.   ObList.FreeObs;     { Free the objects in the ObList, then...      }
  163.   ObList.Free;        { Free the ObList when the main form is closed }
  164. end;
  165.  
  166. { ----- add specific types of objects ----- }
  167. procedure TForm1.AddBookOb;
  168. { add an ordinary BookOb to the ObList and display its data on the List box }
  169. var
  170.   book : BookOb;
  171. begin
  172.   book := BookOb.Create( BookEd.Text, AuthorEd.Text );
  173.   ObList.Add( book );
  174. end;
  175.  
  176. procedure TForm1.AddRefBookOb;
  177. { add a RefBookOb to the ObList and display its data on the List box        }
  178. var
  179.   book : RefBookOb;
  180. begin
  181.   if (ExtraEdit.Text = '') then
  182.         MessageDlg('You must enter a book type (e.g. "Physics" or "Art") ',
  183.                     mtInformation, [mbOk], 0)
  184.   else
  185.   begin
  186.     book := RefBookOb.Create( BookEd.Text, AuthorEd.Text, ExtraEdit.Text );
  187.     ObList.Add( book );
  188.   end;
  189. end;
  190.  
  191. procedure TForm1.AddHorrorBookOb;
  192. { add a HorrorBookOb to the ObList and display its data on the List box     }
  193. var
  194.   book  : HorrorBookOb;
  195.   score : integer;
  196.   ok    : boolean;
  197. begin
  198.   ok := true;
  199.   { First check that a valid gorescore has been entered                     }
  200.   { try to convert the contents of ExtraEdit to an integer                  }
  201.   try
  202.     score := StrToInt(GoreScoreCombo.Text);
  203.   except
  204.      on EConvertError do     { Error if user did not enter an integer }
  205.      begin                   { recover from this by setting score to  }
  206.        score := 0;           { 0 and displaying a message             }
  207.        MessageDlg('Invalid gorescore entry!', mtInformation,[mbOk], 0);
  208.       ok := false;
  209.      end;
  210.   end;
  211.   if ok then                { if the integer is valid, is it 0..10?  }
  212.      if (score < 0) or (score > 10) then
  213.         MessageDlg('The score must be a number from 0 to 10!', mtInformation,
  214.                    [mbOk], 0)
  215.   else
  216.   begin                     { if all is ok, create the object       }
  217.     book := HorrorBookOb.Create( BookEd.Text, AuthorEd.Text, score );
  218.     ObList.Add( book );
  219.   end;
  220. end;
  221.  
  222. procedure TForm1.AddBtnClick(Sender: TObject);
  223. { User wants to add a book object. Determine the type of object         }
  224. { by the Combo box item that's been selected, then call an appropriate  }
  225. { method.                                                               }
  226. begin
  227.   if ((BookEd.Text = '') or (AuthorEd.Text = '')) then
  228.         MessageDlg('You must enter a book and an author!', mtInformation,
  229.       [mbOk], 0)
  230.   else
  231.   if ComboBox1.Text = 'Horror' then
  232.     AddHorrorBookOb
  233.   else
  234.   if ComboBox1.Text = 'Reference' then
  235.     AddRefBookOb
  236.   else
  237.     AddBookOb;
  238. end;
  239.  
  240. procedure TForm1.ShowBtnClick(Sender: TObject);
  241. begin
  242.   ShowObList;
  243. end;
  244.  
  245. procedure TForm1.ClearBtnClick(Sender: TObject);
  246. begin
  247.   ObList.FreeObs;
  248. end;
  249.  
  250. procedure TForm1.ComboBox1Change(Sender: TObject);
  251. { When a change (normally a selection) is made in the ComboBox, see if the    }
  252. { selected item indicates a special type of book. If so, display a label      }
  253. { and text entry field to allow the user to enter the additional data needed  }
  254. { to create an object of the specified type.                                  }
  255. begin
  256.   if ComboBox1.Text = 'Horror' then
  257.   begin
  258.     ExtraLabel.Caption := 'Enter Gore score [1 to 10]';
  259.     ExtraLabel.Show;
  260.     ExtraEdit.Hide;
  261.     GoreScoreCombo.Show;
  262.     GoreScoreCombo.SetFocus;
  263.   end
  264.   else if ComboBox1.Text = 'Reference' then
  265.   begin
  266.     ExtraLabel.Caption := 'Enter the type of reference book';
  267.     ExtraEdit.Text := 'General';
  268.     ExtraLabel.Show;
  269.     ExtraEdit.Show;
  270.     GoreScoreCombo.Hide;
  271.     ExtraEdit.SetFocus;
  272.   end
  273.   else
  274.   begin   { if it's an ordinary book, don't display the extra edit box       }
  275.     ExtraLabel.Hide;
  276.     ExtraEdit.Hide;
  277.     GoreScoreCombo.Hide;
  278.   end;
  279. end;
  280.  
  281.  
  282. // ===========================================
  283. // BOOKOBLIST Start...
  284. // ===========================================
  285. procedure BookObList.SaveObList( fname : string );
  286. var
  287.    fs : TFileStream;
  288.     i : integer;
  289. begin
  290.    fs := TFileStream.Create(fname, fmCreate );
  291.    try
  292.      for i := 0 to ObList.Count - 1 do
  293.          BookOb(ObList[i]).WriteToStream( fs ); { call VIRTUAL method!!! }
  294.    finally;
  295.      fs.Free;
  296.    end;
  297. end;
  298.  
  299. procedure BookObList.LoadObList( fname : string );
  300. var
  301.    fs : TFileStream;
  302.    cn : cnstr;
  303. begin
  304.    ObList.FreeObs;
  305.    fs := TFileStream.Create(fname, fmOpenRead );
  306.    try
  307.     Form1.ListBox1.Items.Add( Format('fs.Position = %d, fs.Size = %d',
  308.                                     [fs.Position,fs.Size] ));
  309.    while fs.Position < fs.Size do
  310.    begin     { read classname and call appropriate constructor }
  311.        fs.ReadBuffer(cn, sizeof(cn));
  312.        if cn = 'HorrorBookOb' then
  313.           ObList.Add(HorrorBookOb.CreateFromStream(cn,fs))
  314.        else if cn = 'RefBookOb' then
  315.           ObList.Add(RefBookOb.CreateFromStream(cn,fs))
  316.        else
  317.           ObList.Add(BookOb.CreateFromStream(cn,fs));
  318.    end;
  319.    finally;
  320.      fs.Free;
  321.    end;
  322. end;
  323.  
  324. procedure BookObList.FreeObs;
  325. { Free the objects in the ObList  }
  326. var
  327.   i : integer;
  328. begin
  329.   for i := 0 to ObList.Count - 1 do
  330.       if ObList.Items[i] <> nil then
  331.       begin
  332.          Form1.ListBox1.Items.Add( 'Call to FREE Ob: ' +
  333.                              BookOb(ObList.Items[i]).Describe );
  334.          BookOb(ObList.Items[i]).Free;
  335.       end;
  336.   ObList.Clear;
  337. end;
  338.  
  339. // constructor
  340. constructor BookObList.Create;
  341. begin
  342.   inherited Create;
  343. end;
  344.  
  345. // destructor
  346. destructor BookObList.Destroy;
  347. begin
  348.    inherited Destroy;
  349. end;
  350. // ...BOOKOBLIST End
  351.  
  352.  
  353.  
  354. // ===========================================
  355. // BOOKOB FAMILY TREE Start...
  356. // ===========================================
  357.  
  358. // BOOKOB
  359.  
  360. constructor BookOb.Create( aName, anAuthor : bkstr );
  361. { a standard BookOb. Call default initialisation, then init the 2 fields }
  362. begin
  363.   inherited Create;
  364.   Form1.ListBox1.Items.Add( 'CONSTRUCTOR: BookOb.Create' );
  365.   cn   := self.className;
  366.   name := aName;
  367.   author := anAuthor;
  368. end;
  369.  
  370. // alternative constructor.
  371. // creates itself using data read from stream
  372. constructor BookOb.CreateFromStream( cname : cnstr; fs: TFileStream);
  373. begin
  374.    inherited Create;
  375.    Form1.ListBox1.Items.Add( 'CONSTRUCTOR: ' + cname + '.CreateFromStream' );
  376.    cn := cname;
  377.    ReadFromStream( fs );
  378. end;
  379.  
  380. destructor BookOb.Destroy;
  381. begin
  382.   Form1.ListBox1.Items.Add( 'DESTRUCTOR: BookOb.Destroy' );
  383.   name := '';
  384.   author := '';
  385.   cn := '';
  386.   inherited Destroy;
  387. end;
  388.  
  389. procedure BookOb.WriteToStream( fs : TFileStream );
  390. begin
  391.   fs.WriteBuffer(cn, sizeof(cn));
  392.   fs.WriteBuffer(author, sizeof(author) );
  393.   fs.WriteBuffer(name, sizeof(name ) );
  394. end;
  395.  
  396. procedure BookOb.ReadFromStream( fs : TFileStream );
  397. begin
  398.   fs.ReadBuffer(author, sizeof(author) );
  399.   fs.ReadBuffer(name, sizeof(name ) );
  400. end;
  401.  
  402. function BookOb.Describe : bkstr;
  403. begin
  404.   result := Format( '[General Book] %s by %s', [name,author]);
  405. end;
  406.  
  407. // HORRORBOOKOB
  408. constructor HorrorBookOb.Create( aName, anAuthor : bkstr; aGorescore : gorerating );
  409. { a HorrorBookOb. Call its ancestor (BookOb) constructor, then init gorescore }
  410. begin
  411.   inherited Create(aName, anAuthor);
  412.   Form1.ListBox1.Items.Add( 'CONSTRUCTOR: HorrorBookOb.Create' );
  413.   gorescore := aGorescore;
  414. end;
  415.  
  416. destructor HorrorBookOb.Destroy;
  417. begin
  418.   Form1.ListBox1.Items.Add( 'DESTRUCTOR: HorrorBookOb.Destroy' );
  419.   gorescore := 0;
  420.   inherited Destroy;
  421. end;
  422.  
  423. procedure HorrorBookOb.WriteToStream( fs : TFileStream );
  424. begin
  425.   fs.WriteBuffer(cn, sizeof(cn));
  426.   fs.WriteBuffer(author, sizeof(author) );
  427.   fs.WriteBuffer(name, sizeof(name ) );
  428.   fs.WriteBuffer(gorescore, sizeof(gorescore ) );
  429. end;
  430.  
  431. procedure HorrorBookOb.ReadFromStream( fs : TFileStream );
  432. begin
  433.   fs.ReadBuffer(author, sizeof(author) );
  434.   fs.ReadBuffer(name, sizeof(name ) );
  435.   fs.ReadBuffer(gorescore, sizeof(gorescore ) );
  436. end;
  437.  
  438. function HorrorBookOb.Describe : bkstr;
  439. begin
  440.   result := Format( '[Horror Book] %s by %s. Gore Score: %d',
  441.                     [name,author,gorescore]);
  442. end;
  443.  
  444. // REFBOOKOB
  445. constructor RefBookOb.Create( aName, anAuthor, aReftype : bkstr );
  446. { a RefBookOb. Call its ancestor (BookOb) constructor, then init reftype      }
  447. begin
  448.   inherited Create(aName, anAuthor);
  449.   Form1.ListBox1.Items.Add( 'CONSTRUCTOR: RefBookOb.Create' );
  450.   reftype := aReftype;
  451. end;
  452.  
  453. destructor RefBookOb.Destroy;
  454. begin
  455.   Form1.ListBox1.Items.Add( 'DESTRUCTOR: RefBookOb.Destroy' );
  456.   reftype := '';
  457.   inherited Destroy;
  458. end;
  459.  
  460. procedure RefBookOb.WriteToStream( fs : TFileStream );
  461. begin
  462.   fs.WriteBuffer(cn, sizeof(cn));
  463.   fs.WriteBuffer(author, sizeof(author) );
  464.   fs.WriteBuffer(name, sizeof(name ) );
  465.   fs.WriteBuffer(reftype, sizeof(reftype ) );
  466. end;
  467.  
  468. procedure RefBookOb.ReadFromStream( fs : TFileStream );
  469. begin
  470.   fs.ReadBuffer(author, sizeof(author) );
  471.   fs.ReadBuffer(name, sizeof(name ) );
  472.   fs.ReadBuffer(reftype, sizeof(reftype ) );
  473. end;
  474.  
  475. function RefBookOb.Describe : bkstr;
  476. begin
  477. result := Format( '[Reference Book] %s by %s. Book type: %s',
  478.                     [name,author,reftype]);
  479. end;
  480.  
  481. // ...BOOKOB FAMILY TREE End
  482.  
  483.  
  484. end.
  485.