分享

Wrapper Classes for Run-Time Type Information (RTTI) UPDATED

 quasiceo 2012-12-08

Wrapper Classes for Run-Time Type Information (RTTI) UPDATED

by Chris Lichti Email: Anonymous


Wrapper Classes for Run-Time Type Information (RTTI) functions in the typinfo.pas unit.
Description
clRTTI is a utility unit that wraps Delphi's Run-Time Type Information in a set of easy-to-use classes:

UPDATE: Added method to check for set membership using strings
eg: if Wrapper['Anchors'].SetHasMember('akLeft') then ...

TrtWrapper
This class takes a class or object in its constructor and provides access to properties in a manner similar to TTable. For example, here is the code to make every control on a form that can support a Popup menu use PopupMenu1:

var
RTObj: TrtWrapper;
i: integer;
begin
for i := 0 to ComponentCount - 1 do
begin
RTObj := TrtWrapper.Create(Components[i]);

if RTObj.HasProperty('PopupMenu') then
RTObj['PopupMenu'].AsObject := PopupMenu1;

RTObj.Free;
end;
end;

And here is the code to get strings for the enumeration values of the Align property of any TControl. Notice that no created instance is required for this:

var
Prop: TrtProperty;
i: integer;
begin
Prop := TrtProperty.Create( TControl, 'Align' );

for i := 0 to Prop.EnumNames.Count - 1 do
begin
ShowMessage( Prop.EnumNames[i] );
end;
end;

TrtProperty
This class represents an individual published property of a class or object. It provides a simple interface for checking attributes of the property, and it provides convenient and familiar 'AsSomething' properties to get and set the Property's value. Here's an extension of the previous example, this time with TrtProperty:

var
RTObj: TrtWrapper;
Prop: TrtProperty;
i, j: integer;
begin
for i := 0 to ComponentCount - 1 do
begin
RTObj := TrtWrapper.Create(Components[i]);

for j := 0 to RTObj.Count - 1 do
begin
Prop := RTObj.Items[j];

//set all fonts to match the form font, regardless of property name
if Prop.Kind = tkClass then
begin
if Prop.PropClassType.InheritsFrom( TFont ) then
Prop.AsObject := Font;
end;
end;

RTObj.Free;
end;
end;

Chris Lichti
Chief Architect, Computer Aided Dispatch
Centurion Solutions
Innovation Through Imagination
http://www.


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unit clRTTI;
{
Author: Chris Lichti
Email:  chlichti@
February 2, 2001

clRTTI is a utility unit that wraps Delphi's Run-Time Type Information in
a set of easy-to-use classes:

TrtWrapper
This class takes a class or object in its constructor and provides access to
properties in a manner similar to TTable.  For example, here is the code to
make every control on a form that can support a Popup menu use PopupMenu1:

  var
    RTObj: TrtWrapper;
    i: integer;
  begin
    for i := 0 to ComponentCount - 1 do
    begin
      RTObj := TrtWrapper.Create(Components[i]);

      if RTObj.HasProperty('PopupMenu') then
        RTObj['PopupMenu'].AsObject := PopupMenu1;

      RTObj.Free;
    end;
  end;

And here is the code to get strings for the enumeration values of the Align
property of any TControl.  Notice that no created instance is required for this:

  var
    Prop: TrtProperty;
    i: integer;
  begin
    Prop := TrtProperty.Create( TControl, 'Align' );

    for i := 0 to Prop.EnumNames.Count - 1 do
    begin
      ShowMessage( Prop.EnumNames[i] );
    end;
  end;

TrtProperty
This class represents an individual published property of a class or object.
It provides a simple interface for checking attributes of the property, and it
provides convenient and familiar 'AsSomething' properties to get and set the
Property's value.  Here's an extension of the previous example, this time with
TrtProperty:

  var
    RTObj: TrtWrapper;
    Prop: TrtProperty;
    i, j: integer;
  begin
    for i := 0 to ComponentCount - 1 do
    begin
      RTObj := TrtWrapper.Create(Components[i]);

      for j := 0 to RTObj.Count - 1 do
      begin
        Prop := RTObj.Items[j];

        //set all fonts to match the form font, regardless of property name
        if Prop.Kind = tkClass then
        begin
          if Prop.PropClassType.InheritsFrom( TFont ) then
            Prop.AsObject := Font;
        end;
      end;

      RTObj.Free;
    end;
  end;
}

interface

uses typinfo, Classes, SysUtils, contnrs;

type
  {
  TrtProperty is a class the represents a single property's RTTI.
  }
  TrtProperty = class
  protected
    FEnumNames: TStringList;
    FSetNames: TStringList;

    function GetAsString: string;
    function GetAsFloat: extended;
    function GetAsInt64: int64;
    function GetAsInteger: integer;
    function GetAsMethod: TMethod;
    function GetAsObject: TObject;
    function GetAsVariant: Variant;
    function GetName: string;
    function GetValue: Variant;
    procedure SetAsFloat( const Value: extended );
    procedure SetAsInt64( const Value: int64 );
    procedure SetAsInteger( const Value: integer );
    procedure SetAsMethod( const Value: TMethod );
    procedure SetAsObject( const Value: TObject );
    procedure SetAsString( const Value: string );
    procedure SetAsVariant( const Value: Variant );
    procedure SetValue( const Value: Variant );

    function GetTypeKind: TTypeKind;
    function GetEnumNames: TStrings;
    function GetSetNames: TStrings;
    function GetPropClassType: TClass;
    function GetIsDelegate: boolean;
    function GetIsStored: boolean;
  public
    Instance: TObject;
    ObjClassType: TClass;
    TypeData: PTypeData;
    TypeInfo: PTypeInfo;
    PropInfo: PPropInfo;
    constructor Create( APropInfo: PPropInfo ); overload; virtual;
    constructor Create( AObject: TObject; propName: string ); overload; virtual;
    constructor Create( AClass: TClass; propName: string ); overload; virtual;
    destructor Destroy; override;

    //Getting and Setting the Property's Value
    property AsString: string read GetAsString write SetAsString;
    property AsInteger: integer read GetAsInteger write SetAsInteger;
    property AsInt64: int64 read GetAsInt64 write SetAsInt64;
    property AsFloat: extended read GetAsFloat write SetAsFloat;
    property AsMethod: TMethod read GetAsMethod write SetAsMethod;
    property AsObject: TObject read GetAsObject write SetAsObject;
    property AsVariant: Variant read GetAsVariant write SetAsVariant;
    property Value: Variant read GetValue write SetValue;

    //Direct access to commonly used type information
    property Name: string read GetName;
    property Kind: TTypeKind read GetTypeKind;
    property EnumNames: TStrings read GetEnumNames;
    property SetNames: TStrings read GetSetNames;
    function SetHasMember( MemberStr: string ): boolean;
    property PropClassType: TClass read GetPropClassType;
    property IsDelegate: boolean read GetIsDelegate;
    property IsStored: boolean read GetIsStored;

    //A helper function to help determine how many characters would be
    //  needed to store all possible values of the property.  This may
    //  only be useful for Enumeration and Set types.
    function MaxStringLen: integer;
  end;

  {
  TrtPropertyList is a container class for TrtProperty instances.  It helps
  TrtWrapper (below) keep track of and clean up after instances of
  TrtPropertyList.
  }
  TrtPropertyList = class( TObjectList )
  private
    function GetItem( Index: Integer ): TrtProperty;
    procedure SetItem( Index: Integer; const Value: TrtProperty );
  public
    constructor Create; virtual;
    function Add( ArtPropObj: TrtProperty ): Integer;
    function Remove( ArtPropObj: TrtProperty ): Integer;
    property Items[Index: Integer]: TrtProperty read GetItem write SetItem; default;
  end;

  {
  TrtWrapper

  TrtWrapper is an object that is meant to parallel any object instance or class
  to providing random access to any of the published property type information
  of that instance or class.

  This class uses RTTI to navigate the published
  properties of an instance or class.  I encapsulates the logic of
  navigating specific property kinds (VisiblePropKinds), while avoiding
  properties of specific names (StopNames)
  }
  TrtWrapper = class
  private
    procedure SetStopNames( const Value: TStrings );
    procedure SetVisiblePropKinds( const Value: TTypeKinds );
    function GetStopNames: TStrings;
  protected
    FVisiblePropKinds: TTypeKinds;
    FStopNames: TStringList;

    FPropObjects: TrtPropertyList;

    function NextAllIndexFromAllIndex( AllIndex: integer ): integer; virtual;
    function CreatePropertyObject( Index: integer ): TrtProperty;
    function GetPropertyObject( Index: integer ): TrtProperty;

    function GetItemByName( Name: string ): TrtProperty;
    function GetItemByIndex( index: integer ): TrtProperty;

    property PropertyObjects[index: integer]: TrtProperty read GetPropertyObject;
  public
    AllCount, Count: integer;
    ObjClass: TClass;
    ObjInstance: TObject;
    ObjTypeData: PTypeData;
    ObjTypeInfo: PTypeInfo;
    ObjPropList: PPropList;
    VisiblePropInfos: array of PPropInfo;

    constructor Create; overload; virtual;
    constructor Create( AClass: TClass; PropKinds: TTypeKinds = [] ); overload; virtual;
    constructor Create( AObject: TObject; PropKinds: TTypeKinds = [] ); overload; virtual;
    destructor Destroy; override;

    procedure Initialize; virtual;

    property VisiblePropKinds: TTypeKinds read FVisiblePropKinds write SetVisiblePropKinds;
    property StopNames: TStrings read GetStopNames write SetStopNames;

    function HasProperty( PropertyName: string ): boolean;
    property ItemsByName[Name: string]: TrtProperty read GetItemByName; default;
    property Items[index: integer]: TrtProperty read GetItemByIndex;
  end;

//Global Utility Functions
//  Find a component in the scope of all forms and data modules.
//    (Adapted from Borland's hidden version)
function rtFindGlobalComponent( const Name: string ): TComponent;
//  Access a singleton TList of all the components in the scope of all forms and data modules.
function rtGlobalComponentList: TList;
//  Convert a type kind into its string representation.
function TypeKindToStr( Kind: TTypeKind ): string;

resourcestring
  ERR_NOINSTANCE = 'This method cannot be called without an object instance.  ' +
    'Construct the RTTI object by passing an object instance to the constructor.';

implementation

uses Forms;

var
  FrtGlobalComponentList: TList; //Singleton accessed by rtGlobalComponentList

//---------- Internal Utility Functions ------------

{
GetSetString
  Since GetSetProp does not initialize Result to an empty string, I must do so
  for it.  silly bug...
}

function GetSetString( Obj: TObject; pInfo: PPropInfo; Brackets: boolean ): string;
begin
  SetLength( Result, 0 );
  Result := GetSetProp( Obj, pInfo, Brackets );
end;

{
GetEnumNamesFromTypeInfo
  Fill the TStrings with the enumeration names described in TypeInfo's TypeData
}

procedure GetEnumNamesFromTypeInfo( TypeInfo: PTypeInfo; sl: TStrings );
var
  TypeData: PTypeData;
  nValue: integer;
begin
  sl.Clear;
  TypeData := GetTypeData( TypeInfo );
  for nValue := TypeData.MinValue to TypeData.MaxValue do
  begin
    sl.AddObject( GetEnumName( TypeInfo, nValue ), TObject( nValue ) );
  end;
end;

//---------- Global Utility Functions ------------

{
rtFindGlobalComponent
  Find a component in the scope of all forms and data modules.
    (Adapted from Borland's hidden version)
}

function rtFindGlobalComponent( const Name: string ): TComponent;
var
  I: Integer;
begin
  for I := 0 to Screen.FormCount - 1 do
  begin
    Result := Screen.Forms[I];
    if not ( csInline in Result.ComponentState ) and
      ( CompareText( Name, Result.Name ) = 0 ) then Exit;
    Result := Result.FindComponent( Name );
    if Assigned( Result ) then Exit;
  end;
  for I := 0 to Screen.DataModuleCount - 1 do
  begin
    Result := Screen.DataModules[I];
    if CompareText( Name, Result.Name ) = 0 then Exit;
    Result := Result.FindComponent( Name );
    if Assigned( Result ) then Exit;
  end;
  Result := nil;
end;

{
rtGlobalComponentList
  Access a singleton TList of all the components in the scope of all forms and
  data modules.
}

function rtGlobalComponentList: TList;
var
  nContainer, nComp: Integer;
  Comp: TComponent;
  List: TList;
  procedure AddComp( AComp: TComponent );
  begin
    if List.IndexOf( AComp ) < 0 then
      List.Add( AComp );
  end;
begin
  if not Assigned( FrtGlobalComponentList ) then
  begin
    FrtGlobalComponentList := TList.Create;
  end;
  List := FrtGlobalComponentList;
  List.Clear;
  for nContainer := 0 to Screen.FormCount - 1 do
  begin
    Comp := Screen.Forms[nContainer];
    AddComp( Comp );
    for nComp := 0 to Comp.ComponentCount - 1 do
    begin
      AddComp( Comp.Components[nComp] );
    end;
  end;
  for nContainer := 0 to Screen.DataModuleCount - 1 do
  begin
    Comp := Screen.DataModules[nContainer];
    AddComp( Comp );
    for nComp := 0 to Comp.ComponentCount - 1 do
    begin
      AddComp( Comp.Components[nComp] );
    end;
  end;
  Result := List;
end;

{
TypeKindToStr
  Convert a type kind into its string representation.
}

function TypeKindToStr( Kind: TTypeKind ): string;
begin
  case Kind of
    tkUnknown: Result := 'tkUnknown';
    tkInteger: Result := 'tkInteger';
    tkChar: Result := 'tkChar';
    tkEnumeration: Result := 'tkEnumeration';
    tkFloat: Result := 'tkFloat';
    tkString: Result := 'tkString';
    tkSet: Result := 'tkSet';
    tkClass: Result := 'tkClass';
    tkMethod: Result := 'tkMethod';
    tkWChar: Result := 'tkWChar';
    tkLString: Result := 'tkLString';
    tkWString: Result := 'tkWString';
    tkVariant: Result := 'tkVariant';
    tkArray: Result := 'tkArray';
    tkRecord: Result := 'tkRecord';
    tkInterface: Result := 'tkInterface';
    tkInt64: Result := 'tkInt64';
    tkDynArray: Result := 'tkDynArray';
  end;
end;

{ TrtWrapper }

{ TrtProperty }

constructor TrtProperty.Create( AObject: TObject; propName: string );
var
  APropInfo: PPropInfo;
begin
  Instance := AObject;
  ObjClassType := AObject.ClassType;
  APropInfo := GetPropInfo( AObject, propName );
  Create( APropInfo );
end;

constructor TrtProperty.Create( APropInfo: PPropInfo );
begin
  PropInfo := APropInfo;
  TypeInfo := APropInfo^.PropType^;
  TypeData := GetTypeData( TypeInfo );
  FEnumNames := nil;
  FSetNames := nil;
end;

function TrtProperty.MaxStringLen: integer;
  function TotalLenWithCommas( sl: TStrings ): integer;
  var
    nStr: integer;
  begin
    Result := 0;
    for nStr := 0 to sl.Count - 1 do
      Result := Result + length( sl[nStr] ) + 1;
  end;
  function MaxStrLen( sl: TStrings ): integer;
  var
    nStr: integer;
  begin
    Result := 0;
    for nStr := 0 to sl.Count - 1 do
      if length( sl[nStr] ) > Result then
        Result := length( sl[nStr] );
  end;
begin
  Result := -1;
  case Kind of
    tkSet: Result := TotalLenWithCommas( SetNames );
    tkEnumeration: Result := MaxStrLen( EnumNames ) + 1;
  end;
end;

constructor TrtProperty.Create( AClass: TClass; propName: string );
var
  APropInfo: PPropInfo;
begin
  APropInfo := GetPropInfo( AClass, propName );
  ObjClassType := AClass;
  Create( APropInfo );
end;

destructor TrtProperty.Destroy;
begin
  if Assigned( FEnumNames ) then FEnumNames.Free;
  if Assigned( FSetNames ) then FSetNames.Free;
  inherited;
end;

function TrtProperty.GetAsFloat: extended;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := GetFloatProp( Instance, PropInfo );
end;

function TrtProperty.GetAsInt64: int64;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := GetInt64Prop( Instance, PropInfo );
end;

function TrtProperty.GetAsInteger: integer;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  case Kind of
    tkEnumeration: Result := GetEnumValue( TypeInfo, GetEnumProp( Instance, PropInfo ) );
  else
    Result := GetOrdProp( Instance, PropInfo );
  end;
end;

function TrtProperty.GetAsMethod: TMethod;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := GetMethodProp( Instance, PropInfo );
end;

function TrtProperty.GetAsObject: TObject;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := GetObjectProp( Instance, PropInfo );
end;

function TrtProperty.GetAsString: string;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  case Kind of
    tkEnumeration: Result := GetEnumProp( Instance, PropInfo );
    tkSet: Result := GetSetString( Instance, PropInfo, true );
  else
    Result := GetStrProp( Instance, PropInfo );
  end;
end;

function TrtProperty.GetAsVariant: Variant;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := GetVariantProp( Instance, PropInfo );
end;

function TrtProperty.GetEnumNames: TStrings;
var
  nValue: integer;
begin
  if not Assigned( FEnumNames ) then
  begin
    FEnumNames := TStringList.Create;
    for nValue := TypeData.MinValue to TypeData.MaxValue do
    begin
      FEnumNames.Add( GetEnumName( TypeInfo, nValue ) );
    end;
  end;
  Result := FEnumNames;
end;

function TrtProperty.GetIsDelegate: boolean;
begin
  Result := TypeData.ClassType.InheritsFrom( TPersistent ) and
    ( not TypeData.ClassType.InheritsFrom( TComponent ) );
end;

function TrtProperty.GetIsStored: boolean;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := IsStoredProp( Instance, PropInfo );
end;

function TrtProperty.GetName: string;
begin
  Result := PropInfo^.Name;
end;

function TrtProperty.GetPropClassType: TClass;
resourcestring
  ERR_MUSTBETKCLASS = 'PropClassType property is only valid for tkClass properties';
begin
  Assert( Kind = tkClass, ERR_MUSTBETKCLASS );
  Result := TypeData.ClassType;
end;

function TrtProperty.SetHasMember( MemberStr: string ): boolean;
resourcestring
  ERR_MUSTBETKSET = 'SetHasMember property is only valid for tkSet properties';
var
  nMemberIdx: integer;
  slSetValue: TStringList;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Assert( Kind = tkSet, ERR_MUSTBETKSET );

  Result := false;
  slSetValue := nil;
  try
    slSetValue := TStringList.Create;

    nMemberIdx := SetNames.IndexOf( MemberStr );
    if nMemberIdx < 0 then
      raise Exception.CreateFmt( 'SetHasMember: Unknown Member String (%s)', [MemberStr] );

    slSetValue.CommaText := Copy(AsString, 2, length(AsString) - 2);

    Result := (slSetValue.IndexOf(MemberStr) > -1);
  finally
    slSetValue.Free;
  end;
end;

function TrtProperty.GetSetNames: TStrings;
var
  enumTypeInfo: PTypeInfo;
begin
  if not Assigned( FSetNames ) then
  begin
    FSetNames := TStringList.Create;
    enumTypeInfo := TypeData^.CompType^;
    if enumTypeInfo^.Kind = tkEnumeration then
      GetEnumNamesFromTypeInfo( enumTypeInfo, FSetNames );
  end;
  Result := FSetNames;
end;

function TrtProperty.GetTypeKind: TTypeKind;
begin
  Result := TypeInfo^.Kind;
end;

function TrtProperty.GetValue: Variant;
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  Result := GetPropValue( Instance, Name );
end;

procedure TrtProperty.SetAsFloat( const Value: extended );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  SetFloatProp( Instance, PropInfo, Value );
end;

procedure TrtProperty.SetAsInt64( const Value: int64 );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  SetInt64Prop( Instance, PropInfo, Value );
end;

procedure TrtProperty.SetAsInteger( const Value: integer );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  case Kind of
    tkEnumeration: SetEnumProp( Instance, PropInfo, GetEnumName( TypeInfo, Value ) );
  else
    SetOrdProp( Instance, PropInfo, Value );
  end;
end;

procedure TrtProperty.SetAsMethod( const Value: TMethod );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  SetMethodProp( Instance, PropInfo, Value );
end;

procedure TrtProperty.SetAsObject( const Value: TObject );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  SetObjectProp( Instance, PropInfo, Value );
end;

procedure TrtProperty.SetAsString( const Value: string );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  case Kind of
    tkEnumeration: SetEnumProp( Instance, PropInfo, Value );
    tkSet: SetSetProp( Instance, propInfo, Value );
  else
    SetStrProp( Instance, PropInfo, Value );
  end;
end;

procedure TrtProperty.SetAsVariant( const Value: Variant );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  SetVariantProp( Instance, PropInfo, Value );
end;

procedure TrtProperty.SetValue( const Value: Variant );
begin
  Assert( Instance <> nil, ERR_NOINSTANCE );
  SetPropValue( Instance, Name, Value );
end;

{ TrtWrapper }

constructor TrtWrapper.Create( AClass: TClass; PropKinds: TTypeKinds );
begin
  ObjClass := AClass;
  ObjInstance := nil;
  ObjTypeInfo := AClass.ClassInfo;
  FVisiblePropKinds := PropKinds;

  Create;
end;

constructor TrtWrapper.Create( AObject: TObject; PropKinds: TTypeKinds );
begin
  ObjInstance := AObject;
  ObjClass := ObjInstance.ClassType;
  ObjTypeInfo := AObject.ClassInfo;
  FVisiblePropKinds := PropKinds;

  Create;
end;

constructor TrtWrapper.Create;
begin
  FStopNames := TStringList.Create;
  FPropObjects := TrtPropertyList.Create;
  ObjPropList := nil;
  Initialize;
end;

{
CreatePropertyObject
Creates a TrtProperty for the specified AllIndex.  If an instance is available,
it uses the instance to create the TrtProperty so that properties of that
instance can be manipulated.  If no instance is available, then it tries to
create the TrtProperty with a class.  If no class is available, then it just
uses the raw propInfo from the ObjPropList.
}

function TrtWrapper.CreatePropertyObject( Index: integer ): TrtProperty;
var
  propInfo: PPropInfo;
  propName: string;
begin
  propInfo := VisiblePropInfos[Index];
  propName := propInfo^.Name;
  if Assigned( ObjInstance ) then
    FPropObjects[Index] := TrtProperty.Create( ObjInstance, propName )
  else if Assigned( ObjClass ) then
    FPropObjects[Index] := TrtProperty.Create( ObjClass, propName )
  else
    FPropObjects[Index] := TrtProperty.Create( propInfo );
  Result := FPropObjects[Index];
end;

destructor TrtWrapper.Destroy;
begin
  FStopNames.Free;
  FPropObjects.Free;

  if Assigned( ObjPropList ) then
    FreeMem( ObjPropList, ObjTypeData^.PropCount * SizeOf( PPropInfo ) );

  inherited;
end;

function TrtWrapper.GetItemByName( Name: string ): TrtProperty;
var
  nProp: integer;
begin
  Result := nil;
  for nProp := 0 to Count - 1 do
  begin
    if CompareText( PropertyObjects[nProp].Name, Name ) = 0 then
    begin
      Result := PropertyObjects[nProp];
      break;
    end;
  end;
end;

function TrtWrapper.GetItemByIndex( index: integer ): TrtProperty;
begin
  Result := PropertyObjects[index];
end;


function TrtWrapper.GetStopNames: TStrings;
begin
  Result := FStopNames;
end;

function TrtWrapper.HasProperty( PropertyName: string ): boolean;
begin
  Result := IsPublishedProp( ObjClass, PropertyName );
end;

procedure TrtWrapper.Initialize;
var
  nHandledCount: integer;
  nAProp: integer;
begin
  //Clean up buffers from previous calls to Initialize...
  if Assigned( ObjPropList ) then
  begin
    FreeMem( ObjPropList, ObjTypeData^.PropCount * SizeOf( PPropInfo ) );
    ObjPropList := nil;
  end;

  FPropObjects.Clear;

  //Prepare buffer for a new property list
  ObjTypeData := GetTypeData( ObjTypeInfo );
  GetMem( ObjPropList, ObjTypeData^.PropCount * SizeOf( PPropInfo ) );

  AllCount := GetPropList( ObjTypeInfo, tkAny, ObjPropList );

  SetLength( VisiblePropInfos, AllCount );

  //Calculate the count of the properties that are VisiblePropKinds and not in
  //  StopNames.  The logic for this is in NextAllIndexFromAllIndex
  nHandledCount := 0;
  nAProp := NextAllIndexFromAllIndex( -1 );
  while nAProp > -1 do
  begin
    VisiblePropInfos[nHandledCount] := ObjPropList[nAProp];
    FPropObjects.Add( nil ); //Place holder for the Property Object

    inc( nHandledCount );
    nAProp := NextAllIndexFromAllIndex( nAProp );
  end;

  SetLength( VisiblePropInfos, nHandledCount );
  Count := nHandledCount;
end;

{
NextAllIndexFromAllIndex
Horrible name, huh?  The idea here is the indexes passed in and returned from
this method are relative to the entire list of properties, not just the ones
that are "visible".  Visible properties are properties that satisfy these
conditions:
  - The property's Kind is in VisiblePropKinds
  - The property's Name is not in StopNames
}

function TrtWrapper.NextAllIndexFromAllIndex( AllIndex: integer ): integer;
var
  propName: string;
  nAllProp: integer;
  propInfo: PPropInfo;
begin
  nAllProp := AllIndex + 1;
  Result := -1;
  while nAllProp < AllCount do
  begin
    propInfo := ObjPropList^[nAllProp];
    propName := ObjPropList^[nAllProp]^.Name;
    if FStopNames.IndexOf( LowerCase( propName ) ) = -1 then
      if ( VisiblePropKinds = [] ) or
        ( propInfo^.PropType^.Kind in VisiblePropKinds ) then
      begin
        Result := nAllProp;
        break;
      end;
    inc( nAllProp );
  end;
end;

{
GetPropertyObject
Gets the TrtProperty at the specified AllIndex.  Creates it if necessary.
}

function TrtWrapper.GetPropertyObject(
  Index: integer ): TrtProperty;
begin
  Result := FPropObjects[Index];
  if not Assigned( Result ) then
  begin
    Result := CreatePropertyObject( Index );
  end;
end;

procedure TrtWrapper.SetStopNames( const Value: TStrings );
var
  nName: integer;
begin
  FStopNames.Assign( Value );
  for nName := 0 to FStopNames.Count - 1 do
    FStopNames[nName] := LowerCase( FStopNames[nName] );
  Initialize;
end;

procedure TrtWrapper.SetVisiblePropKinds( const Value: TTypeKinds );
begin
  FVisiblePropKinds := Value;
  Initialize;
end;

{ TrtPropertyList }

function TrtPropertyList.Add( ArtPropObj: TrtProperty ): Integer;
begin
  Result := inherited Add( ArtPropObj );
end;

constructor TrtPropertyList.Create;
begin
  inherited;
  OwnsObjects := true;
end;

function TrtPropertyList.GetItem( Index: Integer ): TrtProperty;
begin
  Result := TrtProperty( inherited Items[index] );
end;

function TrtPropertyList.Remove( ArtPropObj: TrtProperty ): Integer;
begin
  Result := inherited Remove( ArtPropObj );
end;

procedure TrtPropertyList.SetItem( Index: Integer;
  const Value: TrtProperty );
begin
  inherited Items[index] := Value;
end;

end.



For more information, see http://chlichti.home./



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章