lazarus-ccr/wst/trunk/record_rtti.pas
2015-01-03 13:33:43 +00:00

371 lines
9.2 KiB
ObjectPascal

{
This file is part of the Web Service Toolkit
Copyright (c) 2006 by Inoussa OUEDRAOGO
This file is provide under modified LGPL licence
( the files COPYING.modifiedLGPL and COPYING.LGPL).
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
{$INCLUDE wst_global.inc}
unit record_rtti;
{$RANGECHECKS OFF}
interface
uses
SysUtils, TypInfo, wst_types;
type
PRecordFieldInfo = ^TRecordFieldInfo;
TRecordFieldInfo = packed record
Name : shortstring;
TypeInfo : PPTypeInfo;
Offset : PtrUInt;
IsAttribute : Boolean;
Visible : Boolean;
end;
PRecordTypeData = ^TRecordTypeData;
TRecordTypeData = packed record
Name : shortstring;
RecordSize : PtrUInt;
FieldCount: PtrUInt;
Fields: array [0..0] of TRecordFieldInfo;
end;
{ TRecordRttiDataObject }
TRecordRttiDataObject = class(TDataObject)
public
constructor Create(const AData : PRecordTypeData; const AFieldList : string);
destructor Destroy();override;
function GetRecordTypeData() : PRecordTypeData;{$IFDEF USE_INLINE}inline;{$ENDIF}
function FindField(const AFieldName : shortstring) : PRecordFieldInfo;
function GetField(const AFieldName : shortstring) : PRecordFieldInfo;
end;
function MakeRecordTypeInfo(ARawTypeInfo : PTypeInfo) : PRecordTypeData;
procedure FreeRecordTypeInfo(ATypeInfo : PRecordTypeData);{$IFDEF USE_INLINE}inline;{$ENDIF}
{$IFDEF WST_RECORD_RTTI}
function MakeRawTypeInfo(
const ATypeName : string;
const ATypeSize : PtrUInt;
const AOffset : array of PtrUInt;
const ATypes : array of PTypeInfo
):PTypeInfo ;
{$ENDIF WST_RECORD_RTTI}
procedure initialize_record_rtti();
procedure finalize_record_rtti();
implementation
uses
Classes, imp_utils, wst_consts;
{$IFDEF WST_RECORD_RTTI}
var
RawTypeInfoList : TList = nil;
type
PFieldInfo = ^TFieldInfo;
TFieldInfo = packed record
TypeInfo: PPTypeInfo;
Offset: Cardinal;
end;
PFieldTable = ^TFieldTable;
TFieldTable = packed record
X: Word;
Size: Cardinal;
Count: Cardinal;
Fields: array [0..0] of TFieldInfo;
end;
function MakeRawTypeInfo(
const ATypeName : string;
const ATypeSize : PtrUInt;
const AOffset : array of PtrUInt;
const ATypes : array of PTypeInfo
):PTypeInfo ;
var
i, j, bufferSize, count : LongInt;
delphiFT : PFieldTable;
resBuffer, tmp : PByte;
fieldInfo : PFieldInfo;
typ : PTypeInfo;
begin
count := Length(AOffset);
Assert(count = Length(ATypes));
bufferSize :=
1 + // Kind
1 + Length(ATypeName) +
SizeOf(Word) + // X
SizeOf(Cardinal) + // Size
SizeOf(Cardinal) + // Count
( count * SizeOf(TFieldInfo) );
GetMem(resBuffer,bufferSize);
FillChar(Pointer(resBuffer)^,bufferSize,#0);
tmp := resBuffer;
typ := PTypeInfo(resBuffer);
typ^.Kind := tkRecord;
PByte(@(typ^.Name[0]))^ := Length(ATypeName);
Move(ATypeName[1],typ^.Name[1],Length(ATypeName));
Inc(tmp,SizeOf(TTypeKind)); // Kind
Inc(tmp,1 + Byte(typ^.Name[0])); // Name
delphiFT := PFieldTable(tmp);
delphiFT^.X := 0;
delphiFT^.Size := ATypeSize;
delphiFT^.Count := count;
for i := 1 to count do begin
j := i - 1;
fieldInfo := @(delphiFT^.Fields[j]);
fieldInfo^.Offset := AOffset[j];
GetMem(fieldInfo^.TypeInfo,SizeOf(Pointer));
fieldInfo^.TypeInfo^ := ATypes[j];
end;
Result := typ;
RawTypeInfoList.Add(Result);
end;
procedure FreeRawTypeInfo(ARawTypeInfo : PTypeInfo);
var
i : Cardinal;
delphiFT : PFieldTable;
tmp : PByte;
fieldInfo : PFieldInfo;
begin
if Assigned(ARawTypeInfo) then begin
tmp := PByte(ARawTypeInfo);
Inc(tmp,SizeOf(TTypeKind)); // Kind
Inc(tmp,1 + Byte(ARawTypeInfo^.Name[0])); // Name
delphiFT := PFieldTable(tmp);
for i := 1 to delphiFT^.Count do begin
fieldInfo := @(delphiFT^.Fields[(i - 1)]);
FreeMem(fieldInfo^.TypeInfo);
fieldInfo^.TypeInfo := nil;
end;
FreeMem(ARawTypeInfo);
end;
end;
function MakeRecordTypeInfo(ARawTypeInfo : PTypeInfo) : PRecordTypeData;
var
i, bufferSize, count : LongInt;
delphiFT : PFieldTable;
resBuffer : PRecordTypeData;
fieldInfo : PRecordFieldInfo;
fld : PFieldInfo;
tmp : PByte;
begin
tmp := PByte(ARawTypeInfo);
Inc(tmp);
Inc(tmp,1 + Byte(ARawTypeInfo^.Name[0]));
delphiFT := PFieldTable(tmp);
count := delphiFT^.Count;
{calc buffer size}
bufferSize :=
SizeOf(shortstring) + // Name : shortstring;
SizeOf(PtrUInt) + // Size : PtrUInt;
SizeOf(PtrUInt) + // FieldCount: PtrUInt;
( count * SizeOf(TRecordFieldInfo) ); // Fields: array [0..0] of TRecordFieldInfo;
GetMem(resBuffer,bufferSize);
FillChar(Pointer(resBuffer)^,bufferSize,#0);
resBuffer^.Name := PTypeInfo(ARawTypeInfo)^.Name;
resBuffer^.RecordSize := delphiFT^.Size;
resBuffer^.FieldCount := count;
{ Process elements }
for i := 1 to Count do begin
fld := @(delphiFT^.Fields[(i - 1)]);
fieldInfo := @(resBuffer^.Fields[(i - 1)]);
fieldInfo^.TypeInfo := fld^.TypeInfo;
fieldInfo^.Offset := fld^.Offset;
fieldInfo^.Visible := True;
end;
Result := resBuffer;
end;
{$ENDIF WST_RECORD_RTTI}
{$IFDEF FPC_XXXXXX}
function aligntoptr(p : pointer) : pointer;inline;
begin
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
result:=align(p,sizeof(p));
{$else FPC_REQUIRES_PROPER_ALIGNMENT}
result:=p;
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
end;
function MakeRecordTypeInfo(ARawTypeInfo : PTypeInfo) : PRecordTypeData;
{
A record is designed as follows :
1 : tkrecord
2 : Length of name string (n);
3 : name string;
3+n : record size;
7+n : number of elements (N)
11+n : N times : Pointer to type info
Offset in record
}
var
Temp : pbyte;
namelen : byte;
count,
offset,
i : longint;
//info : pointer;
resBuffer : PRecordTypeData;
typName : shortstring;
typSize : Cardinal;
bufferSize : PtrUInt;
fieldInfo : PRecordFieldInfo;
begin
Temp := PByte(ARawTypeInfo);
Inc(Temp);
{ Skip Name }
namelen := Temp^;
SetLength(typName,namelen);
Inc(temp,1);
Move(Temp^,typName[1],namelen);
Inc(temp,namelen);
temp:=aligntoptr(temp);
{ Skip size }
typSize := PLongint(Temp)^;
Inc(Temp,4);
{ Element count }
Count := PLongint(Temp)^;
Inc(Temp,sizeof(Count));
{calc buffer size}
bufferSize :=
SizeOf(shortstring) + // Name : shortstring;
SizeOf(PtrUInt) + // Size : PtrUInt;
SizeOf(PtrUInt) + // FieldCount: PtrUInt;
( Count * SizeOf(TRecordFieldInfo) ); // Fields: array [0..0] of TRecordFieldInfo;
GetMem(resBuffer,bufferSize);
FillChar(Pointer(resBuffer)^,bufferSize,#0);
resBuffer^.Name := typName;
resBuffer^.RecordSize := typSize;
resBuffer^.FieldCount := count;
{ Process elements }
for i := 1 to Count do begin
fieldInfo := @(resBuffer^.Fields[(i - 1)]);
//Info := PPointer(Temp)^;
fieldInfo^.TypeInfo := PPTypeInfo(Temp);
Inc(Temp,sizeof(Pointer));
Offset := PLongint(Temp)^;
fieldInfo^.Offset := Offset;
fieldInfo^.Visible := True;
Inc(Temp,sizeof(Offset));
end;
Result := resBuffer;
end;
{$ENDIF FPC}
procedure FreeRecordTypeInfo(ATypeInfo : PRecordTypeData);
begin
if ( ATypeInfo <> nil ) then
FreeMem(ATypeInfo);
end;
{ TRecordRttiDataObject }
constructor TRecordRttiDataObject.Create(
const AData : PRecordTypeData;
const AFieldList : string
);
var
locData : PRecordTypeData;
i : Integer;
ls, s : string;
begin
locData := AData;
inherited Create(locData);
ls := Trim(AFieldList);
s := '';
i := 0;
while ( i < locData^.FieldCount ) do begin
s := GetToken(ls,';');
if IsStrEmpty(s) then
Break;
locData^.Fields[i].Name := s;
Inc(i);
end;
end;
destructor TRecordRttiDataObject.Destroy();
begin
FreeRecordTypeInfo(Data);
inherited Destroy();
end;
function TRecordRttiDataObject.GetRecordTypeData() : PRecordTypeData;
begin
Result := PRecordTypeData(Data);
end;
function TRecordRttiDataObject.FindField(const AFieldName : shortstring) : PRecordFieldInfo;
var
i : Integer;
locData : PRecordTypeData;
locField : shortstring;
begin
Result := nil;
locData := PRecordTypeData(Data);
locField := UpperCase(AFieldName);
for i := 0 to Pred(locData^.FieldCount) do begin
if ( locField = UpperCase(locData^.Fields[i].Name) ) then begin
Result := @(locData^.Fields[i]);
Break;
end;
end;
end;
function TRecordRttiDataObject.GetField(const AFieldName : shortstring) : PRecordFieldInfo;
begin
Result := FindField(AFieldName);
if ( Result = nil ) then
raise Exception.CreateFmt(SERR_IsNotAFieldOf,[AFieldName,GetRecordTypeData()^.Name]);
end;
procedure initialize_record_rtti();
begin
{$IFDEF WST_RECORD_RTTI}
if ( RawTypeInfoList = nil ) then
RawTypeInfoList := TList.Create();
{$ENDIF WST_RECORD_RTTI}
end;
procedure finalize_record_rtti();
begin
{$IFDEF WST_RECORD_RTTI}
if ( RawTypeInfoList <> nil ) then begin
while ( RawTypeInfoList.Count > 0 ) do begin
FreeRawTypeInfo(PTypeInfo(RawTypeInfoList.Items[0]));
RawTypeInfoList.Delete(0);
end;
FreeAndNil(RawTypeInfoList);
end;
{$ENDIF WST_RECORD_RTTI}
end;
initialization
initialize_record_rtti();
finalization
finalize_record_rtti();
end.