123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- { ***************************************************************************
- Copyright (c) 2016-2020 Kike Pérez
- Unit : Quick.RTTI.Utils
- Description : Files functions
- Author : Kike Pérez
- Version : 1.4
- Created : 09/03/2018
- Modified : 05/11/2020
- This file is part of QuickLib: https://github.com/exilon/QuickLib
- ***************************************************************************
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- *************************************************************************** }
- unit Quick.RTTI.Utils;
- {$i QuickLib.inc}
- interface
- uses
- SysUtils,
- Quick.Commons,
- TypInfo,
- Rtti;
- type
- TRttiPropertyOrder = (roFirstBase, roFirstInherited);
- TRTTI = class
- private class var
- fCtx : TRttiContext;
- public
- {$IFNDEF FPC}
- class constructor Create;
- class destructor Destroy;
- class function GetField(aInstance : TObject; const aFieldName : string) : TRttiField; overload;
- class function GetField(aTypeInfo : Pointer; const aFieldName : string) : TRttiField; overload;
- class function FieldExists(aTypeInfo : Pointer; const aFieldName : string) : Boolean;
- class function GetFieldValue(aInstance : TObject; const aFieldName : string) : TValue; overload;
- class function GetFieldValue(aTypeInfo : Pointer; const aFieldName: string) : TValue; overload;
- {$ENDIF}
- class function GetProperties(aType : TRttiType; aOrder : TRttiPropertyOrder = roFirstBase) : TArray<TRttiProperty>;
- class function GetType(aTypeInfo : Pointer) : TRttiType;
- class function GetProperty(aInstance : TObject; const aPropertyName : string) : TRttiProperty; overload;
- class function GetProperty(aTypeInfo : Pointer; const aPropertyName : string) : TRttiProperty; overload;
- class function GetPropertyPath(aInstance : TObject; const aPropertyPath : string) : TRttiProperty;
- {$IFNDEF FPC}
- class function GetMemberPath(aInstance: TObject; const aPropertyPath: string): TRttiMember;
- {$ENDIF}
- class function PathExists(aInstance: TObject; const aPropertyPath: string) : Boolean;
- class function GetPathValue(aInstance : TObject; const aPropertyPath : string) : TValue;
- class procedure SetPathValue(aInstance: TObject; const aPropertyPath: string; aValue : TValue);
- class procedure SetPropertyValue(aInstance : TObject; const aPropertyName : string; aValue : TValue);
- class function PropertyExists(aTypeInfo : Pointer; const aPropertyName : string) : Boolean;
- class function GetPropertyValue(aInstance : TObject; const aPropertyName : string) : TValue; overload;
- class function GetPropertyValue(aTypeInfo : Pointer; const aPropertyName : string) : TValue; overload;
- class function GetPropertyValueEx(aInstance: TObject; const aPropertyName: string): TValue;
- {$IFNDEF FPC}
- class function FindClass(const aClassName: string): TClass;
- class function CreateInstance<T>: T; overload;
- class function CreateInstance<T>(const Args: array of TValue): T; overload;
- class function CreateInstance(aBaseClass : TClass): TObject; overload;
- class function CallMethod(aObject : TObject; const aMethodName : string; aParams : array of TValue) : TValue;
- {$ENDIF}
- end;
- ERTTIError = class(Exception);
- TArrayHelper<T> = class
- public
- class function Concat(const Args: array of TArray<T>): TArray<T>; static;
- end;
- implementation
- { TRTTIUtils }
- {$IFNDEF FPC}
- class constructor TRTTI.Create;
- begin
- fCtx := TRttiContext.Create;
- end;
- class function TRTTI.CreateInstance<T>: T;
- begin
- Result := CreateInstance<T>([]);
- end;
- class function TRTTI.CreateInstance<T>(const Args: array of TValue): T;
- var
- value: TValue;
- rtype: TRttiType;
- rmethod: TRttiMethod;
- rinstype: TRttiInstanceType;
- begin
- rtype := fCtx.GetType(TypeInfo(T));
- for rmethod in rtype.GetMethods do
- begin
- if (rmethod.IsConstructor) and (Length(rmethod.GetParameters) = Length(Args) ) then
- begin
- rinstype := rtype.AsInstance;
- value := rmethod.Invoke(rinstype.MetaclassType,Args);
- Result := value.AsType<T>;
- Exit;
- end;
- end;
- end;
- class function TRTTI.CreateInstance(aBaseClass : TClass): TObject;
- var
- value: TValue;
- rtype: TRttiType;
- rmethod: TRttiMethod;
- rinstype: TRttiInstanceType;
- begin
- Result := nil;
- rtype := fCtx.GetType(aBaseClass);
- for rmethod in rtype.GetMethods do
- begin
- if (rmethod.IsConstructor) and (Length(rmethod.GetParameters) = 0) then
- begin
- rinstype := rtype.AsInstance;
- value := rmethod.Invoke(rinstype.MetaclassType,[]);
- Result := value.AsType<TObject>;
- Exit;
- end;
- end;
- end;
- class function TRTTI.CallMethod(aObject : TObject; const aMethodName : string; aParams : array of TValue) : TValue;
- var
- rtype : TRttiType;
- rmethod : TRttiMethod;
- rinstype: TRttiInstanceType;
- begin
- rtype := fCtx.GetType(aObject.ClassInfo);
- for rmethod in rtype.GetMethods do
- begin
- if CompareText(rmethod.Name,aMethodName) = 0 then
- begin
- rinstype := rtype.AsInstance;
- Result := rmethod.Invoke(rinstype.MetaclassType,aParams);
- end;
- end;
- end;
- class destructor TRTTI.Destroy;
- begin
- fCtx.Free;
- end;
- class function TRTTI.FieldExists(aTypeInfo: Pointer; const aFieldName: string): Boolean;
- var
- rtype : TRttiType;
- begin
- rtype := fCtx.GetType(aTypeInfo);
- Result := rtype.GetField(aFieldName) <> nil;
- end;
- class function TRTTI.GetField(aInstance: TObject; const aFieldName: string): TRttiField;
- var
- rtype : TRttiType;
- begin
- Result := nil;
- rtype := fCtx.GetType(aInstance.ClassInfo);
- if rtype <> nil then
- begin
- Result := rtype.GetField(aFieldName);
- end;
- end;
- class function TRTTI.GetField(aTypeInfo: Pointer; const aFieldName: string): TRttiField;
- var
- rtype : TRttiType;
- begin
- Result := nil;
- rtype := fCtx.GetType(aTypeInfo);
- if rtype <> nil then
- begin
- Result := rtype.GetField(aFieldName);
- end;
- end;
- class function TRTTI.GetFieldValue(aInstance : TObject; const aFieldName: string): TValue;
- var
- rfield: TRttiField;
- begin
- rfield := GetField(aInstance,aFieldName);
- if rfield <> nil then Result := rfield.GetValue(aInstance);
- end;
- class function TRTTI.GetFieldValue(aTypeInfo : Pointer; const aFieldName: string): TValue;
- var
- rfield: TRttiField;
- begin
- rfield := GetField(aTypeInfo,aFieldName);
- if rfield <> nil then rfield.GetValue(aTypeInfo);
- end;
- {$ENDIF}
- class function TRTTI.GetProperty(aInstance: TObject; const aPropertyName: string): TRttiProperty;
- var
- rtype : TRttiType;
- begin
- Result := nil;
- rtype := fCtx.GetType(aInstance.ClassInfo);
- if rtype <> nil then Result := rtype.GetProperty(aPropertyName);
- end;
- class function TArrayHelper<T>.Concat(const Args: array of TArray<T>): TArray<T>;
- var
- i, j, out, len: Integer;
- begin
- len := 0;
- for i := 0 to High(Args) do
- len := len + Length(Args[i]);
- SetLength(Result, len);
- out := 0;
- for i := 0 to High(Args) do
- for j := 0 to High(Args[i]) do
- begin
- Result[out] := Args[i][j];
- Inc(out);
- end;
- end;
- class function TRTTI.GetProperties(aType: TRttiType; aOrder: TRttiPropertyOrder = roFirstBase): TArray<TRttiProperty>;
- var
- flat: TArray<TArray<TRttiProperty>>;
- t: TRttiType;
- depth: Integer;
- begin
- if aOrder = TRttiPropertyOrder.roFirstBase then
- begin
- t := aType;
- depth := 0;
- while t <> nil do
- begin
- Inc(depth);
- t := t.BaseType;
- end;
- SetLength(flat, depth);
- t := aType;
- while t <> nil do
- begin
- Dec(depth);
- {$IFNDEF FPC}
- flat[depth] := t.GetDeclaredProperties;
- {$ELSE}
- flat[depth] := t.GetProperties;
- {$ENDIF}
- t := t.BaseType;
- end;
- end
- else
- begin
- t := aType;
- depth := 0;
- while t <> nil do
- begin
- Inc(depth);
- t := t.BaseType;
- end;
- SetLength(flat, depth);
- t := aType;
- depth := 0;
- while t <> nil do
- begin
- {$IFNDEF FPC}
- flat[depth] := t.GetDeclaredProperties;
- {$ELSE}
- flat[depth] := t.GetProperties;
- {$ENDIF}
- Inc(depth);
- t := t.BaseType;
- end;
- end;
- Result := TArrayHelper<TRttiProperty>.Concat(flat);
- end;
- class function TRTTI.GetProperty(aTypeInfo: Pointer; const aPropertyName: string): TRttiProperty;
- var
- rtype : TRttiType;
- begin
- Result := nil;
- rtype := fCtx.GetType(aTypeInfo);
- if rtype <> nil then Result := rtype.GetProperty(aPropertyName);
- end;
- class function TRTTI.GetPropertyPath(aInstance: TObject; const aPropertyPath: string): TRttiProperty;
- var
- prop : TRttiProperty;
- proppath : string;
- propname : string;
- i : Integer;
- value : TValue;
- rtype : TRttiType;
- {$IFNDEF FPC}
- rfield : TRttiField;
- {$ENDIF}
- lastsegment : Boolean;
- begin
- Result := nil;
- proppath := aPropertyPath;
- lastsegment := False;
- rtype := fCtx.GetType(aInstance.ClassType);
- repeat
- i := proppath.IndexOf('.');
- if i > -1 then
- begin
- propname := Copy(proppath,1,i);
- Delete(proppath,1,i+1);
- end
- else
- begin
- propname := proppath;
- lastsegment := True;
- end;
- if rtype.TypeKind = TTypeKind.tkRecord then
- begin
- {$IFNDEF FPC}
- rfield := rtype.GetField(propname);
- if rfield <> nil then value := rfield.GetValue(aInstance);
- {$ELSE}
- raise ERTTIError.Create('FPC not supports record fields in RTTI');
- {$ENDIF}
- end
- else
- begin
- prop := rtype.GetProperty(propname);
- if prop = nil then Exit;
- if lastsegment then Exit(prop)
- else value := prop.GetValue(aInstance);
- end;
- if not lastsegment then
- begin
- if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
- else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
- end;
- until lastsegment;
- Result := nil;
- end;
- {$IFNDEF FPC}
- class function TRTTI.GetMemberPath(aInstance: TObject; const aPropertyPath: string): TRttiMember;
- var
- prop : TRttiProperty;
- proppath : string;
- propname : string;
- i : Integer;
- value : TValue;
- rtype : TRttiType;
- {$IFNDEF FPC}
- rfield : TRttiField;
- {$ENDIF}
- lastsegment : Boolean;
- begin
- Result := nil;
- proppath := aPropertyPath;
- lastsegment := False;
- rtype := fCtx.GetType(aInstance.ClassType);
- repeat
- i := proppath.IndexOf('.');
- if i > -1 then
- begin
- propname := Copy(proppath,1,i);
- Delete(proppath,1,i+1);
- end
- else
- begin
- propname := proppath;
- lastsegment := True;
- end;
- if rtype.TypeKind = TTypeKind.tkRecord then
- begin
- {$IFNDEF FPC}
- rfield := rtype.GetField(propname);
- if rfield <> nil then
- begin
- if lastsegment then Exit(rfield)
- else value := rfield.GetValue(value.GetReferenceToRawData);
- end;
- {$ELSE}
- raise ERTTIError.Create('FPC not supports record fields in RTTI');
- {$ENDIF}
- end
- else
- begin
- prop := rtype.GetProperty(propname);
- if prop = nil then Exit;
- if lastsegment then Exit(prop)
- else value := prop.GetValue(aInstance);
- end;
- if not lastsegment then
- begin
- if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
- else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
- end;
- until lastsegment;
- end;
- {$ENDIF}
- class function TRTTI.PathExists(aInstance: TObject; const aPropertyPath: string) : Boolean;
- var
- proppath : string;
- propname : string;
- i : Integer;
- value : TValue;
- rtype : TRttiType;
- rprop : TRttiProperty;
- {$IFNDEF FPC}
- rfield : TRttiField;
- {$ENDIF}
- lastsegment : Boolean;
- begin
- if not Assigned(aInstance) then Exit(False);
- lastsegment := False;
- proppath := aPropertyPath;
- rtype := fCtx.GetType(aInstance.ClassType);
- repeat
- Result := False;
- i := proppath.IndexOf('.');
- if i > -1 then
- begin
- propname := Copy(proppath,1,i);
- Delete(proppath,1,i+1);
- end
- else
- begin
- propname := proppath;
- lastsegment := True;
- end;
- if rtype.TypeKind = TTypeKind.tkRecord then
- begin
- {$IFNDEF FPC}
- rfield := rtype.GetField(propname);
- if rfield = nil then Exit
- else
- begin
- value := rfield.GetValue(value.GetReferenceToRawData);
- Result := True;
- end;
- {$ELSE}
- raise ERTTIError.Create('FPC not supports record fields in RTTI');
- {$ENDIF}
- end
- else
- begin
- rprop := rtype.GetProperty(propname);
- if rprop = nil then Exit
- else
- begin
- value := rprop.GetValue(aInstance);
- Result := True;
- end;
- end;
- if not lastsegment then
- begin
- if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
- else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
- end;
- until lastsegment;
- end;
- class function TRTTI.GetPathValue(aInstance: TObject; const aPropertyPath: string): TValue;
- var
- proppath : string;
- propname : string;
- i : Integer;
- value : TValue;
- rtype : TRttiType;
- rprop : TRttiProperty;
- {$IFNDEF FPC}
- rfield : TRttiField;
- {$ENDIF}
- lastsegment : Boolean;
- begin
- Result := nil;
- if not Assigned(aInstance) then Exit;
- lastsegment := False;
- proppath := aPropertyPath;
- rtype := fCtx.GetType(aInstance.ClassType);
- {$IFDEF FPC}
- value := aInstance;
- {$ENDIF}
- repeat
- i := proppath.IndexOf('.');
- if i > -1 then
- begin
- propname := Copy(proppath,1,i);
- Delete(proppath,1,i+1);
- end
- else
- begin
- propname := proppath;
- lastsegment := True;
- end;
- if rtype.TypeKind = TTypeKind.tkRecord then
- begin
- {$IFNDEF FPC}
- rfield := rtype.GetField(propname);
- if rfield = nil then raise ERTTIError.CreateFmt('Field "%s" not found in record',[propname])
- else value := rfield.GetValue(value.GetReferenceToRawData);
- {$ELSE}
- raise ERTTIError.Create('FPC not supports record fields in RTTI');
- {$ENDIF}
- end
- else
- begin
- rprop := rtype.GetProperty(propname);
- if rprop = nil then raise ERTTIError.CreateFmt('Property "%s" not found in object',[propname])
- {$IFNDEF FPC}
- else value := rprop.GetValue(aInstance);
- {$ELSE}
- else
- begin
- if rprop.PropertyType.IsInstance then value := GetObjectProp(value.AsObject,propname)
- else value := rprop.GetValue(value.AsObject);
- end;
- {$ENDIF}
- end;
- if not lastsegment then
- begin
- if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
- else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
- end;
- until lastsegment;
- Result := value;
- end;
- class procedure TRTTI.SetPathValue(aInstance: TObject; const aPropertyPath: string; aValue : TValue);
- var
- proppath : string;
- propname : string;
- i : Integer;
- value : TValue;
- rtype : TRttiType;
- rprop : TRttiProperty;
- {$IFNDEF FPC}
- rfield : TRttiField;
- {$ENDIF}
- lastsegment : Boolean;
- begin
- if not Assigned(aInstance) then Exit;
- lastsegment := False;
- proppath := aPropertyPath;
- rtype := fCtx.GetType(aInstance.ClassType);
- repeat
- i := proppath.IndexOf('.');
- if i > -1 then
- begin
- propname := Copy(proppath,1,i);
- Delete(proppath,1,i+1);
- end
- else
- begin
- propname := proppath;
- lastsegment := True;
- end;
- if rtype.TypeKind = TTypeKind.tkRecord then
- begin
- {$IFNDEF FPC}
- rfield := rtype.GetField(propname);
- if rfield = nil then raise ERTTIError.CreateFmt('Field "%s" not found in record',[propname])
- else
- begin
- if lastsegment then rfield.SetValue(value.GetReferenceToRawData,aValue)
- else value := rfield.GetValue(value.GetReferenceToRawData);
- end;
- {$ELSE}
- raise ERTTIError.Create('FPC not supports record fields in RTTI');
- {$ENDIF}
- end
- else
- begin
- rprop := rtype.GetProperty(propname);
- if rprop = nil then raise ERTTIError.CreateFmt('Property "%s" not found in object',[propname])
- else
- begin
- if lastsegment then rprop.SetValue(aInstance,aValue)
- else value := rprop.GetValue(aInstance);
- end;
- end;
- if not lastsegment then
- begin
- if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
- else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
- end;
- until lastsegment;
- end;
- class function TRTTI.GetPropertyValue(aInstance: TObject; const aPropertyName: string): TValue;
- var
- rprop : TRttiProperty;
- begin
- rprop := GetProperty(aInstance,aPropertyName);
- if rprop <> nil then
- begin
- {$IFNDEF FPC}
- Result := rprop.GetValue(aInstance);
- {$ELSE}
- if rprop.PropertyType.IsInstance then Result := GetObjectProp(aInstance,aPropertyName)
- else Result := rprop.GetValue(aInstance);
- {$ENDIF}
- end;
- end;
- class function TRTTI.GetPropertyValue(aTypeInfo: Pointer; const aPropertyName: string): TValue;
- var
- rprop : TRttiProperty;
- begin
- rprop := GetProperty(aTypeInfo,aPropertyName);
- if rprop <> nil then
- begin
- {$IFNDEF FPC}
- Result := rprop.GetValue(aTypeInfo);
- {$ELSE}
- if rprop.PropertyType.IsInstance then Result := GetObjectProp(aTypeInfo,aPropertyName)
- else Result := rprop.GetValue(aTypeInfo);
- {$ENDIF}
- end;
- end;
- class function TRTTI.GetPropertyValueEx(aInstance: TObject; const aPropertyName: string): TValue;
- var
- pinfo : PPropInfo;
- begin
- Result := nil;
- pinfo := GetPropInfo(aInstance,aPropertyName);
- if pinfo = nil then
- begin
- //if not found can be a public property
- Result := GetPropertyValue(aInstance,aPropertyName);
- Exit;
- end;
- case pinfo.PropType^.Kind of
- tkInteger : Result := GetOrdProp(aInstance,pinfo);
- tkInt64 : Result := GetInt64Prop(aInstance,aPropertyName);
- tkFloat : Result := GetFloatProp(aInstance,aPropertyName);
- tkChar : Result := Char(GetOrdProp(aInstance,aPropertyName));
- {$IFDEF FPC}
- tkWString : Result := GetWideStrProp(aInstance,aPropertyName);
- tkSString,
- tkAString,
- {$ELSE}
- tkUString,
- tkWString,
- {$ENDIF}
- tkLString : Result := GetStrProp(aInstance,pinfo);
- {$IFDEF FPC}
- tkEnumeration :Result := GetOrdProp(aInstance,aPropertyName);
- {$ELSE}
- tkEnumeration : Result := GetOrdProp(aInstance,aPropertyName);
- {$ENDIF}
- tkSet : Result := GetSetProp(aInstance,pinfo,True);
- {$IFNDEF FPC}
- tkClass :
- {$ELSE}
- tkBool : Result := Boolean(GetOrdProp(aInstance,pinfo));
- tkObject :
- {$ENDIF} Result := GetObjectProp(aInstance,pinfo);
- tkDynArray : Result := GetDynArrayProp(aInstance,pinfo);
- end;
- end;
- class function TRTTI.GetType(aTypeInfo: Pointer): TRttiType;
- begin
- Result := fCtx.GetType(aTypeInfo);
- end;
- class function TRTTI.PropertyExists(aTypeInfo: Pointer; const aPropertyName: string) : Boolean;
- var
- rtype : TRttiType;
- begin
- Result := False;
- rtype := fCtx.GetType(aTypeInfo);
- if rtype <> nil then Result := rtype.GetProperty(aPropertyName) <> nil;
- end;
- class procedure TRTTI.SetPropertyValue(aInstance: TObject; const aPropertyName: string; aValue: TValue);
- var
- rprop : TRttiProperty;
- begin
- rprop := GetProperty(aInstance,aPropertyName);
- if rprop <> nil then rprop.SetValue(aInstance,aValue);
- end;
- {$IFNDEF FPC}
- class function TRTTI.FindClass(const aClassName: string): TClass;
- var
- rType : TRttiType;
- rList : TArray<TRttiType>;
- begin
- Result := nil;
- rList := fCtx.GetTypes;
- for rType in rList do
- begin
- if (rType.IsInstance) and (aClassName.EndsWith(rType.Name)) then
- begin
- Result := rType.AsInstance.MetaClassType;
- Break;
- end;
- end;
- end;
- {$ENDIF}
- end.
|