Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
Wrapping filters around TStream classes Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 3.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Peter Johnson

In Java there are various predefined stream classes that provide filters for other 
stream classes - the filter classes essentially "wrap" the streams they operate on. 
The filters can often be applied to further filters. This article demonstrates how 
we can do this in Delphi in a way that is extendable - i.e. we can wrap filters 
around other filters. 


First of all, let's look at why we want to do this. Well say you want to write some 
data primitives as text to a stream and the text to be formatted to fit on a page, 
word wrapping properly. Then if we can wrap a filter that formats the primitives 
around another that formats the text and this filter is wrapped round a file stream 
object, then all we have to do is access the methods of the first class and the 
rest of the process happens automatically. 

The approach I've taken is to define a class, TStreamWrapper, that provides a base 
class for any filters that we want to define. Any TStreamWrapper performs it's i/o 
using another TStream object - the wrapped object. The key point is that 
TStreamWrapper is itself derived from TStream, so that it can also wrap other 
TSteamWrapper objects - giving the extensibility we need. TStreamWrapper can also 
cause a wrapped stream to be freed when it is itself freed - allowing the wrapped 
streams to be created "on the fly" when the TStreamWrapper constructor is called. 

There is no additional functionality built in to TStreamWrapper - this is to be 
provided by derived classes. A small example class is demonstrated here. 

First to TStreamWrapper. Here's the class declaration: 

1   type
2     TStreamWrapper = class(TStream)
3     private
4       FBaseStream: TStream; {The "wrapped" stream}
5       FCloseStream: Boolean; {Free wrapped stream on destruction?}
6     protected
7       procedure SetSize(NewSize: Longint); override;
8       {Sets the size of the stream to the given value if the operation is
9       supported by the underlying stream}
10      property BaseStream: TStream read FBaseStream;
11      {Gives access to the underlying stream to descended classes}
12    public
13      constructor Create(const Stream: TStream;
14        const CloseStream: Boolean = False); virtual;
15      {If CloseStream is true the given underlying stream is freed when
16      this object is freed}
17      destructor Destroy; override;
18      // Implementation of abstract methods of TStream
19      function read(var Buffer; Count: Longint): Longint; override;
20      function write(const Buffer; Count: Longint): Longint; override;
21      function Seek(Offset: Longint; Origin: Word): Longint; override;
22    end;
24  //and the implementation is: 
26  constructor TStreamWrapper.Create(const Stream: TStream;
27    const CloseStream: Boolean);
28  begin
29    inherited Create;
30    // Record wrapped stream and if we free it on destruction
31    FBaseStream := Stream;
32    FCloseStream := CloseStream;
33  end;
35  destructor TStreamWrapper.Destroy;
36  begin
37    // Close wrapped stream if required
38    if FCloseStream then
39      FBaseStream.Free;
40    inherited Destroy;
41  end;
43  function Buffer; Count: Integer): Longint;
44  begin
45    // Simply call underlying stream's Read method
46    Result :=, Count);
47  end;
49  function TStreamWrapper.Seek(Offset: Integer; Origin: Word): Longint;
50  begin
51    // Simply call the same method in the wrapped stream
52    Result := FBaseStream.Seek(Offset, Origin);
53  end;
55  procedure TStreamWrapper.SetSize(NewSize: Integer);
56  begin
57    // Set the size property of the wrapped stream
58    FBaseStream.Size := NewSize;
59  end;
61  function TStreamWrapper.write(const Buffer; Count: Integer): Longint;
62  begin
63    // Simply call the same method in the wrapped stream
64    Result := FBaseStream.write(Buffer, Count);
65  end;
67  //We can now derive a small filter class - TStrStream. As it stands it's not 
68  particularly useful, but does demostrate the techniques. The class reads writes 
69  strings (which are preceded by their lengths) to any stream. The declaration is: 
71  type
72    TStrStream = class(TStreamWrapper)
73    public
74      procedure WriteString(AString: string);
75      function ReadString: string;
76    end;
78   //The class is implemented as follows:
80  function TStrStream.ReadString: string;
81  var
82    StrLen: Integer; // the length of the string
83    PBuf: PChar; // buffer to hold the string that is read
84  begin
85    // Get length of string (as 32 bit integer)
86    ReadBuffer(StrLen, SizeOf(Integer));
87    // Now get string
88    // allocate enough memory to hold string
89    GetMem(PBuf, StrLen);
90    try
91      // read chars into buffer and set resulting string
92      ReadBuffer(PBuf^, StrLen);
93      SetString(Result, PBuf, StrLen);
94    finally
95      // deallocate buffer
96      FreeMem(PBuf, StrLen);
97    end;
98  end;
100 procedure TStrStream.WriteString(AString: string);
101 var
102   Len: Integer; // length of string
103 begin
104   // Write out length of string as 32 bit integer
105   Len := Length(AString);
106   WriteBuffer(Len, SizeOf(Integer));
107   // Now write out the string's characters
108   WriteBuffer(PChar(AString)^, Len);
109 end;
111 //The following code should demonstrate how to write a string to a file and read it 
112 back in again. Here we use a file stream that is created on the fly and 
113 automatically closed when we are done. of course you could create the stream and 
114 close it separately. 
116 procedure WriteText(const Txt: string);
117 var
118   TS: TStrStream;
119 begin
120   // This opens stream on a file stream that will be closed when this stream closes
121   TS := TStrStream.Create(TFileStream.Create('test.dat', fmCreate), True);
122   TS.WriteString(Txt);
123   TS.Free; // this closes wrapped file stream
124 end;
126 function ReadText: string;
127 var
128   TS: TStrStream;
129 begin
130   TS := TStrStream.Create(TFileStream.Create('test.dat', fmOpenRead), True);
131   Result := TS.ReadString;
132   TS.Free;
133 end;

The filter in this example provides additional methods to those in TStreamWrapper. 
We can also provide filters that override the Read and Write methods to alter the 
way that files are written. My resource file classes (available for download from 
my website) use this method to allow data to be written to RCDATA resource files - 
the classes take care of maintaining the correct file structure. 

Component Download:

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC