{ *************************************************************************** }
{                                                                             }
{ EControl Common Library                                                     }
{                                                                             }
{ Copyright (c) 2004 - 2006 EControl Ltd., Zaharov Michael                    }
{     www.econtrol.ru                                                         }
{     support@econtrol.ru                                                     }
{                                                                             }
{ *************************************************************************** }
{$I ecSyntEdit.INC}

unit ecRttiUtils;

interface

uses Classes, TypInfo;

function TypeKind(pInfo: PPropInfo): TTypeKind;
function PropName(pInfo: PPropInfo): string;

function ecGetPropList(ATypeInfo: PTypeInfo; TypeKinds: TTypeKinds; var PList: PPropList): integer;

function GetObjProp(Source: TObject; const Prop: string): TObject;
function ExtractObject(Source: TObject; Root: TComponent; var Path: string; WithProp: Boolean): TObject;

implementation

uses SysUtils;

function TypeKind(pInfo: PPropInfo): TTypeKind;
begin
 Result := pInfo{$IFNDEF EC_DOTNET}^{$ENDIF}.PropType{$IFNDEF EC_DOTNET}^^{$ENDIF}.Kind;
end;

function PropName(pInfo: PPropInfo): string;
begin
  Result := string(pInfo{$IFNDEF EC_DOTNET}^{$ENDIF}.Name);
end;

function ecGetPropList(ATypeInfo: PTypeInfo; TypeKinds: TTypeKinds; var PList: PPropList): integer;
begin
  {$IFDEF EC_DOTNET}
    PList := GetPropList(ATypeInfo, TypeKinds, True);
    Result := Length(PList);
  {$ELSE}
    PList := nil;
    Result := GetPropList(ATypeInfo, TypeKinds, nil);
    if Result <> 0  then
     begin
      GetMem(PList, Result * sizeof(Pointer));
      GetPropList(ATypeInfo, TypeKinds, PList);
     end;
  {$ENDIF}
end;

function GetObjProp(Source: TObject; const Prop: string): TObject;
var pInfo: PPropInfo;
    k, Index: integer;
    PN: string;
begin
 Result := nil;
 if Source <> nil then
 try
   // Check for collection item
   PN := Prop;
   k := Pos('[', Prop);
   if k > 0 then
    begin
     Index := StrToInt(Copy(Prop, k + 1, Length(Prop) - k - 1));
     PN := Copy(Prop, 1, k - 1);
    end else Index := -1;

   PInfo := GetPropInfo(Source, PN, [tkClass]);
   if (pInfo <> nil) and (TypeKind(pInfo) = tkClass) then
    begin
      Result := TObject(GetOrdProp(Source, PN));
      if (Index >= 0) and (Result <> nil) then
       if (Result is TCollection) and ((Result as TCollection).Count > Index) then
         Result := (Result as TCollection).Items[Index]
       else
         Result := nil;
    end else Result := nil;
 except
 end
end;

function ExtractObject(Source: TObject; Root: TComponent; var Path: string; WithProp: Boolean): TObject;
var k: integer;
    S: string;
    IsFirst: Boolean;

  function GetObj(const Prop: string; IncProp: Boolean): TObject;
  var k, k1, Index: integer;
  begin
   k := Pos('[', Prop);
   k1 := Pos(']', Prop);
   if (k > 0) and (k1 > k) then // Collection item handling
    begin
     Result := GetObjProp(Source, Copy(Prop, 1, k - 1));
     if (Result <> nil) and (Result is TCollection) then
      begin
       Index := StrToInt(Trim(Copy(Prop, k + 1, k1 - k - 1)));
       if (Index >= 0) and (Index < TCollection(Result).Count) then
        Result := TCollection(Result).Items[Index]
       else
        Result := nil;
      end else Result := nil;
     Exit;
    end;

   if IncProp then
     Result := GetObjProp(Source, Prop)
   else
     Result := nil;

   if Result = nil then
    begin
      if SameText(Prop, 'Owner') and (Source is TComponent) then
        Result := (Source as TComponent).Owner else
      if IsFirst and (Root <> nil) then
        Result := Root.FindComponent(Prop);
      if (Result = nil) and (Source is TComponent) then
        Result := TComponent(Source).FindComponent(Prop);
      if (Result = nil) and (Source <> nil) and (Source is TComponent) then
        Result := (Source as TComponent).FindComponent(Prop);
    end;
  end;

begin
  IsFirst := True;
  k := Pos('.', Path);
  while k > 0 do
   begin
     S := Copy(Path, 1, k - 1);
     Result := GetObj(Copy(Path, 1, k - 1), True);
     if Result = nil then  Exit;
     IsFirst := False;
     Source := Result;
     Path := Copy(Path, k + 1, Length(Path) - k);
     k := Pos('.', Path);
   end;
  if Path = '' then Result := Source else
    begin
      Result := GetObj(Path, not WithProp);
      if Result = nil then Result := Source
        else Path := '';
    end;
end;

end.