mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-16 08:15:53 +01:00
1936fdc225
- cleaning up - added "low-level" serialization based on fields (and not properties) - added $dmvc_classname property handling for fields serialization - added more unittests
330 lines
9.9 KiB
ObjectPascal
330 lines
9.9 KiB
ObjectPascal
unit DuckListU;
|
|
{ *******************************************************************************
|
|
Copyright 2010-2013 Daniele Teti
|
|
|
|
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.
|
|
|
|
******************************************************************************** }
|
|
|
|
interface
|
|
|
|
uses
|
|
RTTI,
|
|
Classes,
|
|
// superobject,
|
|
Generics.Collections,
|
|
SysUtils,
|
|
TypInfo;
|
|
|
|
type
|
|
TDuckTypedList=class;
|
|
|
|
TdormObjectStatus=(osDirty=0, osClean, osUnknown, osDeleted);
|
|
|
|
EdormException=class(Exception)
|
|
|
|
end;
|
|
|
|
EdormValidationException=class(EdormException)
|
|
|
|
end;
|
|
|
|
TdormEnvironment=(deDevelopment, deTest, deRelease);
|
|
TdormObjectOwner=(ooItself, ooParent);
|
|
TdormSaveType=(stAllGraph, stSingleObject);
|
|
TdormRelations=set of (drBelongsTo, drHasMany, drHasOne);
|
|
TdormFillOptions=set of (CallAfterLoadEvent);
|
|
|
|
IList=interface
|
|
['{2A1BCB3C-17A2-4F8D-B6FB-32B2A1BFE840}']
|
|
function Add(const Value: TObject): Integer;
|
|
procedure Clear;
|
|
function Count: Integer;
|
|
function GetItem(index: Integer): TObject;
|
|
end;
|
|
|
|
TdormListEnumerator=class(TEnumerator<TObject>)
|
|
protected
|
|
FPosition: Int64;
|
|
FDuckTypedList: TDuckTypedList;
|
|
|
|
protected
|
|
function DoGetCurrent: TObject; override;
|
|
function DoMoveNext: boolean; override;
|
|
|
|
public
|
|
constructor Create(ADuckTypedList: TDuckTypedList);
|
|
end;
|
|
|
|
TSortingType=(soAscending, soDescending);
|
|
|
|
IWrappedList=interface
|
|
['{B60AF5A6-7C31-4EAA-8DFB-D8BD3E112EE7}']
|
|
function Count: Integer;
|
|
function GetItem(const index: Integer): TObject;
|
|
procedure Add(const AObject: TObject);
|
|
procedure Clear;
|
|
function GetEnumerator: TdormListEnumerator;
|
|
function WrappedObject: TObject;
|
|
procedure Sort(const PropertyName: string; Order: TSortingType=soAscending);
|
|
function GetOwnsObjects: boolean;
|
|
procedure SetOwnsObjects(const Value: boolean);
|
|
property OwnsObjects: boolean read GetOwnsObjects write SetOwnsObjects;
|
|
end;
|
|
|
|
TDuckTypedList=class(TInterfacedObject, IWrappedList)
|
|
protected
|
|
FCTX: TRTTIContext;
|
|
FObjectAsDuck: TObject;
|
|
FAddMethod: TRttiMethod;
|
|
FClearMethod: TRttiMethod;
|
|
FCountProperty: TRttiProperty;
|
|
FGetItemMethod: TRttiMethod;
|
|
FGetCountMethod: TRttiMethod;
|
|
function Count: Integer;
|
|
function GetItem(const index: Integer): TObject;
|
|
procedure Add(const AObject: TObject);
|
|
procedure Clear;
|
|
function WrappedObject: TObject;
|
|
procedure QuickSort(List: IWrappedList; L, R: Integer; SCompare: TFunc<TObject, TObject, Integer>); overload;
|
|
|
|
procedure QuickSort(List: IWrappedList; SCompare: TFunc<TObject, TObject, Integer>); overload;
|
|
procedure Sort(const PropertyName: string; Order: TSortingType=soAscending);
|
|
|
|
public
|
|
constructor Create(AObjectAsDuck: TObject);
|
|
destructor Destroy; override;
|
|
function GetEnumerator: TdormListEnumerator;
|
|
function GetOwnsObjects: boolean;
|
|
procedure SetOwnsObjects(const Value: boolean);
|
|
property OwnsObjects: boolean read GetOwnsObjects write SetOwnsObjects;
|
|
class function CanBeWrappedAsList(const AObjectAsDuck: TObject): boolean;
|
|
end;
|
|
|
|
function WrapAsList(const AObject: TObject): IWrappedList;
|
|
|
|
implementation
|
|
|
|
uses System.Math,
|
|
RTTIUtilsU;
|
|
|
|
constructor TdormListEnumerator.Create(ADuckTypedList: TDuckTypedList);
|
|
begin
|
|
inherited Create;
|
|
FDuckTypedList := ADuckTypedList;
|
|
FPosition := -1;
|
|
end;
|
|
|
|
function TdormListEnumerator.DoGetCurrent: TObject;
|
|
begin
|
|
if FPosition>-1 then
|
|
Result := FDuckTypedList.GetItem(FPosition)
|
|
else
|
|
raise Exception.Create('Enumerator error: Call MoveNext first');
|
|
end;
|
|
|
|
function TdormListEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
if FPosition<FDuckTypedList.Count-1 then
|
|
begin
|
|
Inc(FPosition);
|
|
Result := True;
|
|
end
|
|
else
|
|
Result := false;
|
|
end;
|
|
|
|
function TDuckTypedList.GetEnumerator: TdormListEnumerator;
|
|
begin
|
|
Result := TdormListEnumerator.Create(self);
|
|
end;
|
|
|
|
procedure TDuckTypedList.Add(const AObject: TObject);
|
|
begin
|
|
FAddMethod.Invoke(FObjectAsDuck, [AObject]);
|
|
end;
|
|
|
|
class function TDuckTypedList.CanBeWrappedAsList(const AObjectAsDuck: TObject): boolean;
|
|
var
|
|
FCTX: TRTTIContext;
|
|
begin
|
|
Result := (FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Add')<>nil)and(FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Clear')<>nil)
|
|
|
|
{$IF CompilerVersion >= 23}
|
|
and(FCTX.GetType(AObjectAsDuck.ClassInfo).GetIndexedProperty('Items').ReadMethod<>nil)
|
|
|
|
{$IFEND}
|
|
and((FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetItem')<>nil)or(FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetElement')<>
|
|
nil))and(FCTX.GetType(AObjectAsDuck.ClassInfo).GetProperty('Count')<>nil)
|
|
|
|
end;
|
|
|
|
procedure TDuckTypedList.Clear;
|
|
begin
|
|
FClearMethod.Invoke(FObjectAsDuck, []);
|
|
end;
|
|
|
|
function TDuckTypedList.Count: Integer;
|
|
begin
|
|
if Assigned(FCountProperty) then
|
|
Result := FCountProperty.GetValue(FObjectAsDuck).AsInteger
|
|
else
|
|
Result := FGetCountMethod.Invoke(FObjectAsDuck, []).AsInteger;
|
|
|
|
end;
|
|
|
|
constructor TDuckTypedList.Create(AObjectAsDuck: TObject);
|
|
begin
|
|
inherited Create;
|
|
FObjectAsDuck := AObjectAsDuck;
|
|
FAddMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Add');
|
|
if not Assigned(FAddMethod) then
|
|
raise EdormException.Create('Cannot find method "Add" in the duck object');
|
|
FClearMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Clear');
|
|
if not Assigned(FClearMethod) then
|
|
raise EdormException.Create('Cannot find method "Clear" in the duck object');
|
|
FGetItemMethod := nil;
|
|
|
|
{$IF CompilerVersion >= 23}
|
|
FGetItemMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetIndexedProperty('Items').ReadMethod;
|
|
|
|
{$IFEND}
|
|
if not Assigned(FGetItemMethod) then
|
|
FGetItemMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetItem');
|
|
if not Assigned(FGetItemMethod) then
|
|
FGetItemMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetElement');
|
|
if not Assigned(FGetItemMethod) then
|
|
raise EdormException.Create
|
|
('Cannot find method Indexed property "Items" or method "GetItem" or method "GetElement" in the duck object');
|
|
FCountProperty := FCTX.GetType(AObjectAsDuck.ClassInfo).GetProperty('Count');
|
|
if not Assigned(FCountProperty) then
|
|
begin
|
|
FGetCountMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Count');
|
|
if not Assigned(FGetCountMethod) then
|
|
|
|
raise EdormException.Create('Cannot find property/method "Count" in the duck object');
|
|
end;
|
|
end;
|
|
|
|
destructor TDuckTypedList.Destroy;
|
|
begin
|
|
|
|
inherited;
|
|
end;
|
|
|
|
function TDuckTypedList.GetItem(const index: Integer): TObject;
|
|
begin
|
|
Result := FGetItemMethod.Invoke(FObjectAsDuck, [index]).AsObject;
|
|
end;
|
|
|
|
function TDuckTypedList.GetOwnsObjects: boolean;
|
|
begin
|
|
Result := TRTTIUtils.GetProperty(FObjectAsDuck, 'OwnsObjects').AsBoolean
|
|
end;
|
|
|
|
function TDuckTypedList.WrappedObject: TObject;
|
|
begin
|
|
Result := FObjectAsDuck;
|
|
end;
|
|
|
|
function WrapAsList(const AObject: TObject): IWrappedList;
|
|
begin
|
|
try
|
|
Result := TDuckTypedList.Create(AObject);
|
|
except
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TDuckTypedList.QuickSort(List: IWrappedList; L, R: Integer; SCompare: TFunc<TObject, TObject, Integer>);
|
|
var
|
|
I, J: Integer;
|
|
p: TObject;
|
|
begin
|
|
{ 07/08/2013: This method is based on QuickSort procedure from
|
|
Classes.pas, (c) Borland Software Corp.
|
|
but modified to be part of TDuckListU unit. It implements the
|
|
standard quicksort algorithm,
|
|
delegating comparison operation to an anonimous.
|
|
The Borland version delegates to a pure function
|
|
pointer, which is problematic in some cases. }
|
|
repeat
|
|
I := L;
|
|
J := R;
|
|
p := List.GetItem((L+R) shr 1);
|
|
repeat
|
|
while SCompare(TObject(List.GetItem(I)), p)<0 do
|
|
Inc(I);
|
|
while SCompare(TObject(List.GetItem(J)), p)>0 do
|
|
Dec(J);
|
|
if I<=J then
|
|
begin
|
|
TRTTIUtils.MethodCall(List.WrappedObject, 'Exchange', [I, J]);
|
|
Inc(I);
|
|
Dec(J);
|
|
end;
|
|
until I>J;
|
|
if L<J then
|
|
QuickSort(List, L, J, SCompare);
|
|
L := I;
|
|
until I>=R;
|
|
end;
|
|
|
|
procedure TDuckTypedList.QuickSort(List: IWrappedList; SCompare: TFunc<TObject, TObject, Integer>);
|
|
begin
|
|
QuickSort(List, 0, List.Count-1, SCompare);
|
|
end;
|
|
|
|
function CompareValue(const Left, Right: TValue): Integer;
|
|
begin
|
|
if Left.IsOrdinal then
|
|
begin
|
|
Result := System.Math.CompareValue(Left.AsOrdinal, Right.AsOrdinal);
|
|
end
|
|
else if Left.Kind=tkFloat then
|
|
begin
|
|
Result := System.Math.CompareValue(Left.AsExtended, Right.AsExtended);
|
|
end
|
|
else if Left.Kind in [tkString, tkUString, tkWString, tkLString] then
|
|
begin
|
|
Result := CompareText(Left.AsString, Right.AsString);
|
|
end
|
|
else
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
procedure TDuckTypedList.SetOwnsObjects(const Value: boolean);
|
|
begin
|
|
TRTTIUtils.SetProperty(FObjectAsDuck, 'OwnsObjects', Value);
|
|
end;
|
|
|
|
procedure TDuckTypedList.Sort(const PropertyName: string; Order: TSortingType);
|
|
begin
|
|
if Order=soAscending then
|
|
QuickSort(self,
|
|
function(Left, Right: TObject): Integer
|
|
begin
|
|
Result := CompareValue(TRTTIUtils.GetProperty(Left, PropertyName), TRTTIUtils.GetProperty(Right, PropertyName));
|
|
end)
|
|
else
|
|
QuickSort(self,
|
|
function(Left, Right: TObject): Integer
|
|
begin
|
|
Result := -1*CompareValue(TRTTIUtils.GetProperty(Left, PropertyName), TRTTIUtils.GetProperty(Right, PropertyName));
|
|
end);
|
|
end;
|
|
|
|
end.
|