⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 frxxmlserializer.pas

📁 报表源码 FastReport 3 is new generation of the report generators components. It consists of report engin
💻 PAS
字号:
{******************************************}
{ }
{ FastReport v3.0 }
{ XML serializer }
{ }
{ Copyright (c) 1998-2005 }
{ by Alexander Tzyganenko, }
{ Fast Reports Inc. }
{ }
{******************************************}

unit frxXMLSerializer;

interface

{$I frx.inc}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  TypInfo, frxXML, frxClass
{$IFDEF Delphi6}
, Variants
{$ENDIF};

type

{ TfrxXMLSerializer is the XML analogue of the Delphi component streaming-
  TReader and TWriter }

  TfrxXMLSerializer = class(TObject)
  private
    FErrors:TStringList;
    FFixups:TList;
    FOwner:TfrxComponent;
    FReader:TReader;
    FReaderStream:TMemoryStream;
    FSerializeDefaultValues:Boolean;
    FStream:TStream;
    procedure AddFixup(Obj:TPersistent; p:PPropInfo; Value:String);
    procedure ClearFixups;
    procedure FixupReferences;
    procedure OneProp(Name, Value:String; Obj:TPersistent);
  public
    constructor Create(Stream:TStream);
    destructor Destroy; override;
    function ObjToXML(Obj:TPersistent; const Add:String = ''):String;
    function ReadComponent(Root:TfrxComponent):TfrxComponent;
    function ReadComponentStr(Root:TfrxComponent; s:String):TfrxComponent;
    function WriteComponentStr(c:TfrxComponent):String;
    procedure ReadRootComponent(Root:TfrxComponent; XMLItem:TfrxXMLItem = nil;
      DontCreateComponents:Boolean = False);
    procedure ReadPersistentStr(Root:TComponent; Obj:TPersistent; const s:String);
    procedure WriteComponent(c:TfrxComponent);
    procedure WriteRootComponent(Root:TfrxComponent; SaveChildren:Boolean = True;
      XMLItem:TfrxXMLItem = nil);
    procedure XMLToObj(s:String; Obj:TPersistent);
    property Errors:TStringList read FErrors;
    property Owner:TfrxComponent read FOwner write FOwner;
    property Stream:TStream read FStream;
    property SerializeDefaultValues:Boolean read FSerializeDefaultValues
      write FSerializeDefaultValues;
  end;

implementation

uses frxUtils, frxRes;

type
  TfrxFixupItem = class(TObject)
  public
    Obj:TPersistent;
    PropInfo:PPropInfo;
    Value:String;
  end;

  THackComponent = class(TComponent);
  THackPersistent = class(TPersistent);
  THackReader = class(TReader);

{ TfrxXMLSerializer }

constructor TfrxXMLSerializer.Create(Stream:TStream);
begin
  FErrors:= TStringList.Create;
  FErrors.Sorted:= True;
  FErrors.Duplicates:= dupIgnore;
  FFixups:= TList.Create;
  FStream:= Stream;
  FReaderStream:= TMemoryStream.Create;
  FReader:= TReader.Create(FReaderStream, 4096);
end;

destructor TfrxXMLSerializer.Destroy;
begin
  FErrors.Free;
  FReader.Free;
  FReaderStream.Free;
  ClearFixups;
  FFixups.Free;
  inherited;
end;

procedure TfrxXMLSerializer.ClearFixups;
begin
  while FFixups.Count > 0 do
  begin
    TfrxFixupItem(FFixups[0]).Free;
    FFixups.Delete(0);
  end;
end;

procedure TfrxXMLSerializer.AddFixup(Obj:TPersistent; p:PPropInfo;
  Value:String);
var
  Item:TfrxFixupItem;
begin
  Item:= TfrxFixupItem.Create;
  Item.Obj:= Obj;
  Item.PropInfo:= p;
  Item.Value:= Value;
  FFixups.Add(Item);
end;

procedure TfrxXMLSerializer.FixupReferences;
var
  i:Integer;
  Item:TfrxFixupItem;
  Ref:TObject;
begin
  for i:= 0 to FFixups.Count-1 do
  begin
    Item:= FFixups[i];
    Ref:= nil;
    if FOwner<>nil then
      Ref:= FOwner.FindObject(Item.Value);
    if Ref = nil then
      Ref:= frxFindComponent(FOwner, Item.Value);
    if Ref<>nil then
      SetOrdProp(Item.Obj, Item.PropInfo, Integer(Ref));
  end;

  FReader.FixupReferences;
  FReader.EndReferences;
  ClearFixups;
end;

procedure TfrxXMLSerializer.OneProp(Name, Value:String; Obj:TPersistent);
var
  i, code:Integer;
  p:PPropInfo;

  procedure DoNonPublishedProps;
  begin
    FReaderStream.Clear;
    frxStringToStream(Value, FReaderStream);
    FReaderStream.Position:= 0;
    FReader.Position:= 0;

    try
      while FReader.Position < FReaderStream.Size do
        THackReader(FReader).ReadProperty(Obj);
    except
    end;
  end;

begin
  if Length(Name) = 1 then
  begin
// special properties
    case Name[1] of
      'x':
        begin
          TfrxMemoView(Obj).Text:= frxXMLToStr(Value);
          Exit;
        end;
      'l':Name:= 'Left';
      't':Name:= 'Top';
      'w':Name:= 'Width';
      'h':Name:= 'Height';
    end;
  end
  else
  begin
// multiple properties
    i:= Pos('.', Name);
    while i<>0 do
    begin
      p:= GetPropInfo(Obj.ClassInfo, Copy(Name, 1, i-1));
      if p = nil then
        Exit;
      Obj:= TPersistent(GetOrdProp(Obj, p));
      Delete(Name, 1, i);
      i:= Pos('.', Name);
    end;

    if (Obj is TStrings) and (Name = 'text') then
    begin
      TStrings(Obj).Text:= frxXMLToStr(Value);
      Exit;
    end
    else if Name = 'propdata' then
    begin
      DoNonPublishedProps;
      Exit;
    end;
  end;

  p:= GetPropInfo(Obj.ClassInfo, Name);
  if p<>nil then
    case p.PropType^.Kind of
      tkInteger, tkSet, tkChar, tkWChar:
        SetOrdProp(Obj, p, StrToInt(Value));

      tkEnumeration:
        begin
          Val(Value, i, code);
          if code = 0 then
            SetOrdProp(Obj, p, i) else
            SetOrdProp(Obj, p, GetEnumValue(p.PropType^, Value));
        end;

      tkFloat:
        SetFloatProp(Obj, p, frxStrToFloat(Value));

      tkString, tkLString, tkWString:
        SetStrProp(Obj, p, frxXMLToStr(Value));

      tkClass:
        AddFixup(Obj, p, Value);

      tkVariant:
        SetVariantProp(Obj, p, frxXMLToStr(Value));
    end;
end;

procedure TfrxXMLSerializer.XMLToObj(s:String; Obj:TPersistent);
var
  i, j:Integer;
  Name, Value:String;
begin
  while s<>'' do
  begin
    i:= Pos('"', s);
    Name:= Trim(LowerCase(Copy(s, 1, i-2)));
    if Name = '' then break;
    s[i]:= ' ';
    j:= Pos('"', s);
    Value:= Copy(s, i+1, j-i-1);

    try
      OneProp(Name, Value, Obj);
    except
      on E:Exception do
        FErrors.Add(E.Message);
    end;

    Delete(s, 1, j+1);
  end;
end;

function TfrxXMLSerializer.ObjToXML(Obj:TPersistent; const Add:String = ''):String;
var
  TypeInfo:PTypeInfo;
  PropCount:Integer;
  PropList:PPropList;
  i:Integer;
  s:String;
  Flag:Boolean;

  procedure DoOrdProp;
  var
    Value:Integer;
  begin
    Value:= GetOrdProp(Obj, PropList[i]);
    if (Value<>PropList[i].Default) or FSerializeDefaultValues then
      if PropList[i].PropType^.Kind = tkEnumeration then
        s:= GetEnumName(PropList[i].PropType^, Value) else
        s:= IntToStr(Value);
  end;

  procedure DoFloatProp;
  var
    Value:Extended;
  begin
    Value:= GetFloatProp(Obj, PropList[i]);
// commented out due to bug with tfrxmemoview.linespacing=0
// if (Value<>0) or FSerializeDefaultValues then
      s:= FloatToStr(Value);
  end;

  procedure DoStrProp;
  var
    Value:String;
  begin
    Value:= GetStrProp(Obj, PropList[i]);
    if (Value<>'') or FSerializeDefaultValues then
      s:= frxStrToXML(Value);
  end;

  procedure DoVariantProp;
  var
    Value:Variant;
  begin
    Value:= GetVariantProp(Obj, PropList[i]);
    s:= frxStrToXML(VarToStr(Value));
  end;

  procedure DoClassProp;
  var
    FClass:TClass;
    FComp:TComponent;
    FObj:TPersistent;
  begin
    FClass:= GetTypeData(PropList[i].PropType^).ClassType;
    if FClass.InheritsFrom(TComponent) then
    begin
      FComp:= TComponent(GetOrdProp(Obj, PropList[i]));
      if FComp<>nil then
        s:= frxGetFullName(FOwner, FComp);
    end
    else if FClass.InheritsFrom(TPersistent) then
    begin
      FObj:= TPersistent(GetOrdProp(Obj, PropList[i]));
      if FObj is TStrings then
      begin
        s:= TStrings(FObj).Text;
        if (Length(s) >= 2) and
           (s[Length(s)-1] = #13) and (s[Length(s)] = #10) then
          Delete(s, Length(s)-1, 2);
        s:= ' '+Add+PropList[i].Name+'.Text="'+
          frxStrToXML(s)+'"';
      end
      else
        s:= ObjToXML(FObj, Add+PropList[i].Name+'.');
      Flag:= True;
    end;
  end;

  procedure DoNonPublishedProps;
  var
    wr:TWriter;
    ms:TMemoryStream;
  begin
    ms:= TMemoryStream.Create;
    try
      wr:= TWriter.Create(ms, 4096);
      wr.Root:= FOwner;

      try
        THackPersistent(Obj).DefineProperties(wr);
      finally
        wr.Free;
      end;

      if ms.Size > 0 then
      begin
        s:= frxStreamToString(ms);
        Result:= Result+' '+Add+'PropData="'+s+'"';
      end;
    finally
      ms.Free;
    end;
  end;

begin
  Result:= '';
  TypeInfo:= Obj.ClassInfo;
  PropCount:= GetTypeData(TypeInfo).PropCount;
  GetMem(PropList, PropCount * SizeOf(PPropInfo));
  GetPropInfos(TypeInfo, PropList);

  try
    if Obj is TfrxComponent then
      TfrxComponent(Obj).IsWriting:= True;
    for i:= 0 to PropCount-1 do
    begin
      s:= '';
      Flag:= False;

      if IsStoredProp(Obj, PropList[i]) then
        case PropList[i].PropType^.Kind of
          tkInteger, tkSet, tkChar, tkWChar, tkEnumeration:
            DoOrdProp;

          tkFloat:
            DoFloatProp;

          tkString, tkLString, tkWString:
            DoStrProp;

          tkClass:
            DoClassProp;

          tkVariant:
            DoVariantProp;
        end;

      if s<>'' then
        if Flag then
          Result:= Result+s else
          Result:= Result+' '+Add+PropList[i].Name+'="'+s+'"';
    end;
    DoNonPublishedProps;

  finally
    if Obj is TfrxComponent then
      TfrxComponent(Obj).IsWriting:= False;
    FreeMem(PropList, PropCount * SizeOf(PPropInfo));
  end;
end;

procedure TfrxXMLSerializer.ReadRootComponent(Root:TfrxComponent;
  XMLItem:TfrxXMLItem = nil; DontCreateComponents:Boolean = False);
var
  XMLDoc:TfrxXMLDocument;
  CompList:TList;

  procedure DoRead(Item:TfrxXMLItem; Owner:TfrxComponent);
  var
    i:Integer;
    c:TfrxComponent;
  begin
    try
      FindClass(Item.Name);
    except
      FErrors.Add(frxResources.Get('xrCantFindClass')+' '+Item.Name);
      Exit;
    end;

    if Owner<>nil then
    begin
      if DontCreateComponents then
      begin
        c:= FOwner.FindComponent(Item.Prop['Name']) as TfrxComponent;
      end
      else
      begin
        c:= TfrxComponent(FindClass(Item.Name).NewInstance);
        c.Create(Owner);
      end;
    end
    else
      c:= Root;

    c.IsLoading:= True;
    XMLToObj(Item.Text, c);
    CompList.Add(c);

    for i:= 0 to Item.Count-1 do
      DoRead(Item[i], c);
  end;

  procedure DoLoaded;
  var
    i:Integer;
    c:TfrxComponent;
  begin
    for i:= 0 to CompList.Count-1 do
    begin
      c:= CompList[i];
      c.IsLoading:= False;
      if not (c is TfrxReport) then
        THackComponent(c).Loaded;
    end;
  end;

begin
  if Owner = nil then
    Owner:= Root;
  XMLDoc:= nil;
  CompList:= TList.Create;

  if XMLItem = nil then
  begin
    XMLDoc:= TfrxXMLDocument.Create;
    XMLItem:= XMLDoc.Root;
    XMLDoc.LoadFromStream(FStream);
  end;

  FReader.Root:= FOwner;
  FReader.BeginReferences;
  try
    DoRead(XMLItem, nil);
    FixupReferences;
    DoLoaded;
  finally
    if XMLDoc<>nil then
      XMLDoc.Free;
    CompList.Free;
  end;
end;

procedure TfrxXMLSerializer.WriteRootComponent(Root:TfrxComponent;
  SaveChildren:Boolean = True; XMLItem:TfrxXMLItem = nil);
var
  XMLDoc:TfrxXMLDocument;

  procedure DoWrite(Item:TfrxXMLItem; Root:TfrxComponent);
  var
    i:Integer;
  begin
    Item.Name:= Root.ClassName;
    Item.Text:= 'Name="'+Root.Name+'"'+ObjToXML(Root);

    if SaveChildren then
      for i:= 0 to Root.Objects.Count-1 do
        DoWrite(Item.Add, Root.Objects[i]);
  end;

begin
  if Owner = nil then
    Owner:= Root;
  XMLDoc:= nil;

  if XMLItem = nil then
  begin
    XMLDoc:= TfrxXMLDocument.Create;
    XMLItem:= XMLDoc.Root;
    XMLDoc.AutoIndent:= True;
  end;

  try
    DoWrite(XMLItem, Root);
    if XMLDoc<>nil then
      XMLDoc.SaveToStream(FStream);
  finally
    if XMLDoc<>nil then
      XMLDoc.Free;
  end;
end;

function TfrxXMLSerializer.ReadComponent(Root:TfrxComponent):TfrxComponent;
var
  rd:TfrxXMLReader;
  RootItem:TfrxXMLItem;
begin
  rd:= TfrxXMLReader.Create(FStream);
  RootItem:= TfrxXMLItem.Create;

  try
    rd.ReadRootItem(RootItem, False);
    Result:= ReadComponentStr(Root, RootItem.Name+' '+RootItem.Text);
  finally
    rd.Free;
    RootItem.Free;
  end;
end;

procedure TfrxXMLSerializer.WriteComponent(c:TfrxComponent);
var
  s:String;
begin
  s:= '<'+WriteComponentStr(c)+'/>';
  FStream.Write(s[1], Length(s));
end;

function TfrxXMLSerializer.ReadComponentStr(Root:TfrxComponent;
  s:String):TfrxComponent;
var
  n:Integer;
  s1:String;
begin
  Owner:= Root;
  if Trim(s) = '' then
    Result:= nil
  else
  begin
    n:= Pos(' ', s);
    s1:= Copy(s, n+1, MaxInt);
    Delete(s, n, MaxInt);

    Result:= TfrxComponent(FindClass(s).NewInstance);
    Result.Create(Root);

    FReader.Root:= Root;
    FReader.BeginReferences;
    try
      Result.IsLoading:= True;
      XMLToObj(s1, Result);
    finally
      FixupReferences;
      Result.IsLoading:= False;
      if not (Result is TfrxReport) then
        THackComponent(Result).Loaded;
    end;
  end;
end;

function TfrxXMLSerializer.WriteComponentStr(c:TfrxComponent):String;
begin
  Result:= c.ClassName+ObjToXML(c);
end;

procedure TfrxXMLSerializer.ReadPersistentStr(Root:TComponent;
  Obj:TPersistent; const s:String);
begin
  FReader.Root:= Root;
  FReader.BeginReferences;
  XMLToObj(s, Obj);
  FixupReferences;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -