FastReport_2022_VCL/LibD28/frxProtocolFactory.pas
2024-01-01 16:13:08 +01:00

304 lines
7.0 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport VCL }
{ }
{ Copyright (c) 1998-2021 }
{ by Fast Reports Inc. }
{ }
{******************************************}
unit frxProtocolFactory;
interface
{$I frx.inc}
uses
{$IFNDEF FPC}
Windows, Messages,
{$ENDIF}
{$IFDEF FPC}
LResources, LCLType, LazHelper, LMessages,
{$ENDIF}
SysUtils, Classes, SyncObjs;
type
TfrxDataLinkLoadMethod = (dlmGetLink, dlmOnGetData, dlmOnLoading);
IfrxDataLinkObject = interface
['{83CD083D-0399-44F2-B0F7-3A3F8109EE0E}']
function LoadDataStream(Stream: TStream; const NewLink: String): Boolean;
function GetLink(LoadMethod: TfrxDataLinkLoadMethod): String;
function IsExpressionLink: Boolean;
end;
TfrxCustomDatalinkProtocol = class
public
class function LoadBy(var Link: String; Stream: TStream; BoundData: TObject = nil): Boolean; virtual;
end;
TfrxCustomDatalinkProtocolClass = class of TfrxCustomDatalinkProtocol;
TfrxProtocolItem = class
private
FProtocolName: String;
FProtocol: TfrxCustomDatalinkProtocolClass;
FData: TObject;
public
property ProtocolName: String read FProtocolName write FProtocolName;
property Data: TObject read FData write FData;
property Protocol: TfrxCustomDatalinkProtocolClass read FProtocol write FProtocol;
end;
TfrxDataLinkProtocols = class
private
FListCS: TCriticalSection;
FList: TList;
FWriteLock: Boolean;
FReadLockCounter: Integer;
function GetItem(Index: Integer): TfrxProtocolItem;
function IndexOf(const ProtocolName: String): Integer;
procedure Lock(IsWriting: Boolean = False);
procedure Unlock(IsWriting: Boolean = False);
procedure Clear;
procedure ParseLink(var Protocol: String; var Link: String);
public
constructor Create;
destructor Destroy; override;
procedure Register(AClass: TfrxCustomDatalinkProtocolClass; const ProtocolName: String; BoundData: TObject = nil);
procedure Unregister(const ProtocolName: String);
function LoadByLink(var Link: String; Stream: TStream): Boolean;
function LoadToObject(var Link: String; DataLinkObject: IfrxDataLinkObject): Boolean;
function GetProtocol(const Link: String): TfrxCustomDatalinkProtocolClass;
function GetProtocolItem(const Link: String): TfrxProtocolItem;
property Items[Index: Integer]: TfrxProtocolItem read GetItem; default;
end;
function frxDataProtocols: TfrxDataLinkProtocols;
implementation
uses frxXML, frxDataLinkInPlaceEditor;
var
GDataProtocols: TfrxDataLinkProtocols = nil;
function frxDataProtocols: TfrxDataLinkProtocols;
begin
if GDataProtocols = nil then
GDataProtocols := TfrxDataLinkProtocols.Create;
Result := GDataProtocols;
end;
{ TfrxDatallnkProtocols }
procedure TfrxDataLinkProtocols.Clear;
var
i: Integer;
begin
Lock(True);
try
for i := 0 to FList.Count - 1 do
TObject(FList[i]).Free;
FList.Clear;
finally
Unlock(True);
end;
end;
constructor TfrxDataLinkProtocols.Create;
begin
FList := TList.Create;
FListCS := TCriticalSection.Create;
end;
destructor TfrxDataLinkProtocols.Destroy;
begin
Clear;
FreeAndNil(FList);
FreeAndNil(FListCS);
inherited;
end;
function TfrxDataLinkProtocols.GetItem(Index: Integer): TfrxProtocolItem;
begin
Result := TfrxProtocolItem(FList[Index]);
end;
function TfrxDataLinkProtocols.GetProtocol(
const Link: String): TfrxCustomDatalinkProtocolClass;
var
Item: TfrxProtocolItem;
begin
Item := GetProtocolItem(Link);
if Assigned(Item) then
Result := Item.Protocol
else
Result := nil;
end;
function TfrxDataLinkProtocols.GetProtocolItem(
const Link: String): TfrxProtocolItem;
var
LProtocol, LLink: String;
i: Integer;
begin
Result := nil;
LLink := Link;
ParseLink(LProtocol, LLink);
Lock;
try
i := IndexOf(LProtocol);
if i < 0 then Exit;
Result := Items[i];
finally
Unlock;
end;
end;
function TfrxDataLinkProtocols.IndexOf(const ProtocolName: String): Integer;
var
i: Integer;
begin
Result := -1;
Lock;
try
for i := 0 to FList.Count - 1 do
begin
if SameText(TfrxProtocolItem(FList[i]).ProtocolName, ProtocolName) then
begin
Result := i;
break;
end;
end;
finally
Unlock;
end;
end;
function TfrxDataLinkProtocols.LoadByLink(var Link: String; Stream: TStream): Boolean;
var
Item: TfrxProtocolItem;
begin
Result := False;
Item := GetProtocolItem(Link);
if not Assigned(Item) then Exit;
Result := Item.Protocol.LoadBy(Link, Stream, Item.Data);
end;
function TfrxDataLinkProtocols.LoadToObject(var Link: String;
DataLinkObject: IfrxDataLinkObject): Boolean;
var
m: TMemoryStream;
Item: TfrxProtocolItem;
begin
Result := False;
Item := GetProtocolItem(Link);
if not Assigned(DataLinkObject) or not Assigned(Item) then Exit;
m := TMemoryStream.Create;
try
if Item.Protocol.LoadBy(Link, m, Item.Data) then
begin
m.Position := 0;
Result := DataLinkObject.LoadDataStream(m, Link);
end;
except
Result := False;
end;
m.Free;
end;
procedure TfrxDataLinkProtocols.Lock(IsWriting: Boolean);
begin
if IsWriting then
FWriteLock := True
else
begin
Inc(FReadLockCounter);
if FReadLockCounter = 1 then
FListCS.Enter;
end;
if FWriteLock then
FListCS.Enter;
end;
procedure TfrxDataLinkProtocols.ParseLink(var Protocol, Link: String);
var
Index: Integer;
begin
Protocol := '';
Index := Pos('://', Link);
if Index <= 0 then Exit;
Protocol := Copy(Link, 1, Index - 1);
Delete(Link, 1, Index + 2);
end;
procedure TfrxDataLinkProtocols.Register(
AClass: TfrxCustomDatalinkProtocolClass; const ProtocolName: String; BoundData: TObject = nil);
var
Item: TfrxProtocolItem;
begin
if AClass = nil then Exit;
Item := TfrxProtocolItem.Create;
Item.Protocol := AClass;
Item.ProtocolName := ProtocolName;
Item.Data := BoundData;
Lock(True);
try
FList.Add(Item);
finally
Unlock(True);
end;
end;
procedure TfrxDataLinkProtocols.Unlock(IsWriting: Boolean);
begin
if FWriteLock then
FListCS.Leave
else
begin
if FReadLockCounter = 1 then
FListCS.Leave;
Dec(FReadLockCounter);
if FReadLockCounter < 0 then
FReadLockCounter := 0;
end;
if IsWriting then
FWriteLock := False;
end;
procedure TfrxDataLinkProtocols.Unregister(const ProtocolName: String);
var
Index: Integer;
Item: TfrxProtocolItem;
begin
Index := IndexOf(ProtocolName);
if Index > -1 then
begin
Item := TfrxProtocolItem(FList[Index]);
Lock(True);
try
FList.Delete(Index);
finally
Unlock(True);
end;
Item.Free;
end;
end;
{ TfrxCustomDatalinkProtocol }
class function TfrxCustomDatalinkProtocol.LoadBy(var Link: String; Stream: TStream; BoundData: TObject): Boolean;
begin
raise EAbstractError.Create(Format('Method LoadBy in %s', [ClassName]));
end;
initialization
finalization
FreeAndNil(GDataProtocols);
end.