diff --git a/tests/tests.lpi b/tests/tests.lpi
index 7a2aae7..4aa06dc 100644
--- a/tests/tests.lpi
+++ b/tests/tests.lpi
@@ -37,7 +37,7 @@
-
+
@@ -186,6 +186,10 @@
+
+
+
+
@@ -214,9 +218,6 @@
-
diff --git a/tests/tests.lpr b/tests/tests.lpr
index a190b9f..96de6fc 100644
--- a/tests/tests.lpr
+++ b/tests/tests.lpr
@@ -9,7 +9,7 @@ uses
// test cases
uutlAlgorithmTests, uutlEnumeratorTests, uutlHashSetTests, uutlLinqTests,
uutlListTest, uutlMapTests, uutlQueueTests, uutlStackTests, uutlObservableListTests,
- uutlObservableHashSetTests, uutlObservableMapTests
+ uutlObservableHashSetTests, uutlObservableMapTests,
// test misc
uTestHelper,
@@ -17,7 +17,7 @@ uses
// units unter test
uutlAlgorithm, uutlArrayContainer, uutlCommon, uutlComparer, uutlEnumerator,
uutlFilter, uutlGenerics, uutlInterfaces, uutlLinq, uutlListBase, uutlLogger,
- uutlStreamHelper, uutlSyncObjs, uutlTypes, uutlXmlHelper, uutlObservable;
+ uutlStreamHelper, uutlSyncObjs, uutlTypes, uutlXmlHelper, uutlObservable, uutlSetHelperTests;
{$R *.res}
diff --git a/tests/tests.lps b/tests/tests.lps
index 5640e46..4ad6489 100644
--- a/tests/tests.lps
+++ b/tests/tests.lps
@@ -4,47 +4,43 @@
-
+
-
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
-
+
-
-
-
-
-
+
+
+
+
-
+
-
-
+
@@ -53,63 +49,61 @@
-
+
-
+
-
-
+
-
+
-
-
+
-
+
-
+
-
+
-
-
+
-
+
-
+
-
+
+
@@ -117,26 +111,24 @@
-
+
-
-
-
-
-
+
+
+
+
-
+
-
-
+
@@ -144,16 +136,15 @@
-
+
-
+
-
-
+
@@ -162,23 +153,21 @@
-
+
-
+
-
-
+
-
-
-
+
+
@@ -186,7 +175,7 @@
-
+
@@ -194,7 +183,7 @@
-
+
@@ -202,7 +191,7 @@
-
+
@@ -210,7 +199,7 @@
-
+
@@ -219,7 +208,7 @@
-
+
@@ -228,7 +217,7 @@
-
+
@@ -237,7 +226,7 @@
-
+
@@ -246,7 +235,7 @@
-
+
@@ -255,7 +244,7 @@
-
+
@@ -264,15 +253,14 @@
-
+
-
+
-
-
+
@@ -280,16 +268,15 @@
-
+
-
+
-
-
+
@@ -298,7 +285,7 @@
-
+
@@ -307,15 +294,14 @@
-
+
-
+
-
-
+
@@ -370,11 +356,10 @@
-
+
-
-
+
@@ -394,18 +379,17 @@
-
-
-
-
+
+
+
+
-
-
-
+
+
@@ -452,8 +436,8 @@
-
-
+
+
@@ -486,11 +470,10 @@
-
+
-
-
+
@@ -499,6 +482,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -508,133 +515,139 @@
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
-
+
-
+
diff --git a/tests/uutlSetHelperTests.pas b/tests/uutlSetHelperTests.pas
new file mode 100644
index 0000000..6a29bff
--- /dev/null
+++ b/tests/uutlSetHelperTests.pas
@@ -0,0 +1,76 @@
+unit uutlSetHelperTests;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, TestFramework;
+
+type
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ TutlSetHelperTests = class(TTestCase)
+ published
+ procedure ToString;
+ procedure TryToSet;
+ procedure Compare;
+ end;
+
+implementation
+
+uses
+ uutlGenerics;
+
+type
+ TTestEnum = (
+ teTest0 = 0,
+ teTest1 = 1,
+ teTest2 = 2,
+ teTest3 = 3,
+ teTest4 = 4,
+ teTest5 = 5,
+ teTest8 = 8,
+ teTest9 = 9
+ );
+ TTestSet = set of TTestEnum;
+ TTestSetH = specialize TutlSetHelper;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TutlSetHelperTests////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TutlSetHelperTests.ToString;
+var
+ str: String;
+begin
+ str := TTestSetH.ToString([teTest0, teTest1, teTest2, teTest3, teTest4, teTest8]);
+ AssertEquals('teTest0, teTest1, teTest2, teTest3, teTest4, teTest8', str);
+
+ str := TTestSetH.ToString([teTest0, teTest1, teTest2, teTest3, teTest4, teTest8], '_');
+ AssertEquals('teTest0_teTest1_teTest2_teTest3_teTest4_teTest8', str);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TutlSetHelperTests.TryToSet;
+var
+ s: TTestSet;
+begin
+ AssertTrue(TTestSetH.TryToSet('teTest0, teTest1, teTest2, teTest3, teTest8, teTest9', s));
+ AssertTrue([teTest0, teTest1, teTest2, teTest3, teTest8, teTest9] = s);
+
+ AssertTrue(TTestSetH.TryToSet('teTest0_asd_teTest1_asd_teTest2_asd_teTest3_asd_teTest8_asd_teTest9', '_asd_', s));
+ AssertTrue([teTest0, teTest1, teTest2, teTest3, teTest8, teTest9] = s);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TutlSetHelperTests.Compare;
+begin
+ AssertEquals( 0, TTestSetH.Compare([teTest0, teTest1, teTest2], [teTest0, teTest1, teTest2]));
+ AssertEquals(-1, TTestSetH.Compare([ teTest1, teTest2], [teTest0, teTest1, teTest2]));
+ AssertEquals( 1, TTestSetH.Compare([teTest0, teTest1, teTest2], [teTest0, teTest2]));
+end;
+
+initialization
+ RegisterTest(TutlSetHelperTests.Suite);
+
+end.
+
diff --git a/uutlCommon.pas b/uutlCommon.pas
index 18c2d34..0a5b360 100644
--- a/uutlCommon.pas
+++ b/uutlCommon.pas
@@ -79,9 +79,10 @@ type
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function Supports (const aInstance: TObject; const aClass: TClass; out aObj): Boolean;
-function GetTickCount64 (): QWord;
-function GetMicroTime (): QWord;
+function Supports (const aInstance: TObject; const aClass: TClass; out aObj): Boolean;
+function GetTickCount64 (): QWord;
+function GetMicroTime (): QWord;
+function GetPlatformIdentitfier(): String;
function utlRateLimited (const Reference: QWord; const Interval: QWord): boolean;
procedure utlFinalizeObject (var obj; const aTypeInfo: PTypeInfo; const aFreeObject: Boolean);
@@ -150,7 +151,7 @@ var
begin
pc := 0;
QueryPerformanceCounter(pc);
- Result:= (pc * 1000*1000) div PERF_FREQ;
+ result := (pc * 1000*1000) div PERF_FREQ;
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -186,6 +187,68 @@ begin
end;
{$ENDIF}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function GetPlatformIdentitfier: String;
+
+ {$IFDEF WINDOWS}
+ function GetWindowsVersionStr(const aDefault: String): string;
+ var
+ osv: TOSVERSIONINFO;
+ ver: cardinal;
+ begin
+ result := aDefault;
+ osv.dwOSVersionInfoSize := SizeOf(osv);
+ if GetVersionEx(osv) then begin
+ ver := MAKELONG(osv.dwMinorVersion, osv.dwMajorVersion);
+ // positive overflow: if system is newer, always detect as newest we knew instead of failing
+ if ver >= $000A0000 then
+ result := '10'
+ else if ver >= $00060003 then
+ result := '8_1'
+ else if ver >= $00060002 then
+ result := '8'
+ else if ver >= $00060001 then
+ result := '7'
+ else if ver >= $00060000 then
+ result := 'Vista'
+ else if ver >= $00050002 then
+ result := '2003'
+ else if ver >= $00050001 then
+ result := 'XP'
+ else if ver >= $00050000 then
+ result := '2000'
+ else if ver >= $00040000 then
+ result := 'NT4';
+ // ignore NT3, hmkay?;
+ end;
+ end;
+ {$ENDIF}
+
+var
+ os,ver,arch: string;
+begin
+ result := '';
+ os := '';
+ ver := 'generic';
+ arch := '';
+ {$IF DEFINED(WINDOWS)}
+ os := 'mswin';
+ ver := GetWindowsVersionStr(ver);
+ {$ELSEIF DEFINED(LINUX)}
+ os := 'linux';
+ {$Warning System Version String missing!}
+ {$ENDIF}
+
+ {$IF DEFINED(CPUX86)}
+ arch := 'x86';
+ {$ELSEIF DEFINED(cpux86_64)}
+ arch := 'x64';
+ {$ELSE}
+ {$ERROR Unknown Architecture!}
+ {$ENDIF}
+ result := Format('%s-%s-%s', [os, ver, arch]);
+end;
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function utlRateLimited(const Reference: QWord; const Interval: QWord): boolean;
begin
@@ -413,7 +476,7 @@ var
s: String;
e: TFilterEntry;
begin
- Result:= '';
+ result := '';
if (aIncludeAllSupported>'') and (fFilters.Count > 0) then begin
s:= '';
for e in fFilters do begin
@@ -442,7 +505,7 @@ function TFilterBuilderImpl.Add(aDescr, aMask: string; const aAppendFilterToDesc
var
e: TFilterEntry;
begin
- Result:= Self;
+ result := Self;
e:= TFilterEntry.Create;
if aAppendFilterToDesc then
e.Descr:= Format('%s (%s)', [aDescr, aMask])
@@ -459,9 +522,9 @@ var
begin
c:= Pos('|', aFilter);
if c > 0 then
- Result:= (Self as IutlFilterBuilder).Add(Copy(aFilter, 1, c-1), Copy(aFilter, c+1, Maxint))
+ result := (Self as IutlFilterBuilder).Add(Copy(aFilter, 1, c-1), Copy(aFilter, c+1, Maxint))
else
- Result:= (Self as IutlFilterBuilder).Add(aFilter, aFilter, false);
+ result := (Self as IutlFilterBuilder).Add(aFilter, aFilter, false);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/uutlEnumerator.pas b/uutlEnumerator.pas
index d9f3a02..2efbf86 100644
--- a/uutlEnumerator.pas
+++ b/uutlEnumerator.pas
@@ -234,6 +234,7 @@ type
{$ENDIF}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IFDEF UTL_ENUMERATORS}
generic TutlSkipTakeEnumerator = class(
specialize TutlEnumerator)
@@ -255,6 +256,7 @@ type
constructor Create(aEnumerator: IutlEnumerator; const aSkip: Integer; const aTake: Integer);
destructor Destroy; override;
end;
+{$ENDIF}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
@@ -360,6 +362,7 @@ type
{$ENDIF}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IFDEF UTL_ENUMERATORS}
generic TutlConcatEnumerator = class(
specialize TutlEnumerator)
@@ -381,6 +384,7 @@ type
constructor Create(aEnumerators: TEnumerators);
destructor Destroy; override;
end;
+{$ENDIF}
implementation
@@ -696,6 +700,7 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TutlDistinctEnumerator////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlDistinctEnumerator.InternalMoveNext: Boolean;
type
TBinarySearch = specialize TutlBinarySearch;
@@ -723,7 +728,6 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlDistinctEnumerator.GetCurrent: T;
begin
result := fCurrent;
@@ -741,6 +745,7 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TutlIntersectWithoutEnumerator////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlIntersectWithoutEnumerator.InternalMoveNext: Boolean;
type
TBinarySearch = specialize TutlBinarySearch;
@@ -764,7 +769,6 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlIntersectWithoutEnumerator.GetCurrent: T;
begin
result := fCurrent;
@@ -788,6 +792,7 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TutlSkipTakeEnumerator////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IFDEF UTL_ENUMERATORS}
function TutlSkipTakeEnumerator.InternalMoveNext: Boolean;
begin
while (fCurrentSkip > 0) and fEnumerator.MoveNext do
@@ -834,10 +839,12 @@ begin
fEnumerator := nil;
inherited Destroy;
end;
+{$ENDIF}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TutlWhereEnumerator///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlWhereEnumerator.InternalMoveNext: Boolean;
begin
repeat
@@ -852,7 +859,6 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlWhereEnumerator.GetCurrent: T;
begin
result := fEnumerator.Current;
@@ -882,6 +888,7 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TutlSelectEnumerator//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlSelectEnumerator.InternalMoveNext: Boolean;
begin
result := fEnumerator.MoveNext;
@@ -894,7 +901,6 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-{$IF DEFINED(UTL_ENUMERATORS) AND DEFINED(UTL_ADVANCED_ENUMERATORS)}
function TutlSelectEnumerator.GetCurrent: Tout;
begin
result := fSelector.Select(fEnumerator.Current);
@@ -1017,6 +1023,7 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TutlConcatEnumerator//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+{$IFDEF UTL_ENUMERATORS}
function TutlConcatEnumerator.InternalMoveNext: Boolean;
begin
if (fCurrent < 0) then begin
@@ -1065,6 +1072,7 @@ begin
SetLength(fEnumerators, 0);
inherited Destroy;
end;
+{$ENDIF}
end.
diff --git a/uutlGenerics.pas b/uutlGenerics.pas
index c903921..e440915 100644
--- a/uutlGenerics.pas
+++ b/uutlGenerics.pas
@@ -466,13 +466,14 @@ type
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- EutlEnumConvert = class(EConvertError)
+ EEnumConvertException = class(EConvertError)
public
constructor Create(const aValue, aExpectedType: String);
end;
generic TutlEnumHelper = class
public type
+ TEnumType = T;
TValueArray = array of T;
TStringArray = array of String;
@@ -482,18 +483,39 @@ type
fNames: TStringArray;
public
- class function ToString(aValue: T): String; reintroduce;
- class function TryToEnum(aStr: String; out aValue: T): Boolean;
- class function ToEnum(aStr: String): T; overload;
- class function ToEnum(aStr: String; const aDefault: T): T; overload;
+ class function ToString (aValue: T): String; reintroduce;
+ class function TryToEnum (aStr: String; out aValue: T): Boolean;
+ class function ToEnum (aStr: String): T; overload;
+ class function ToEnum (aStr: String; const aDefault: T): T; overload;
- class function Values: TValueArray; inline;
- class function Names: TStringArray; inline;
- class function TypeInfo: PTypeInfo; inline;
+ class function Values: TValueArray; inline;
+ class function Names: TStringArray; inline;
+ class function TypeInfo: PTypeInfo; inline;
class constructor Initialize;
end;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ generic TutlSetHelper = class
+ public type
+ TEnumHelper = specialize TutlEnumHelper;
+ TEnumType = TEnum;
+ TSetType = TSet;
+
+ private
+ class function IsSet (constref aSet: TSet; aEnum: TEnum): Boolean;
+ class procedure SetValue (var aSet: TSet; aEnum: TEnum);
+ class procedure ClearValue(var aSet: TSet; aEnum: TEnum);
+
+ public
+ class function ToString (const aValue: TSet; const aSeperator: String = ', '): String; reintroduce;
+ class function TryToSet (const aStr: String; out aValue: TSet): Boolean; overload;
+ class function TryToSet (const aStr: String; const aSeperator: String; out aValue: TSet): Boolean; overload;
+ class function ToSet (const aStr: String; const aDefault: TSet): TSet; overload;
+ class function ToSet (const aStr: String): TSet; overload;
+ class function Compare (const aSet1, aSet2: TSet): Integer;
+ end;
+
implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1464,7 +1486,7 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//EutlEnumConvert///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-constructor EutlEnumConvert.Create(const aValue, aExpectedType: String);
+constructor EEnumConvertException.Create(const aValue, aExpectedType: String);
begin
inherited Create(Format('%s is not a %s', [aValue, aExpectedType]));
end;
@@ -1505,7 +1527,7 @@ end;
class function TutlEnumHelper.ToEnum(aStr: String): T;
begin
if not TryToEnum(aStr, result) then
- raise EutlEnumConvert.Create(aStr, TypeInfo^.Name);
+ raise EEnumConvertException.Create(aStr, TypeInfo^.Name);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1585,5 +1607,120 @@ begin
SetLength(fNames, High(fNames));
end;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TutlSetHelper/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.IsSet(constref aSet: TSet; aEnum: TEnum): Boolean;
+begin
+ result := ((PByte(@aSet)[Integer(aEnum) shr 3] and (1 shl (Integer(aEnum) and 7))) <> 0);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class procedure TutlSetHelper.SetValue(var aSet: TSet; aEnum: TEnum);
+begin
+ PByte(@aSet)[Integer(aEnum) shr 3] := PByte(@aSet)[Integer(aEnum) shr 3] or (1 shl (Integer(aEnum) and 7));
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class procedure TutlSetHelper.ClearValue(var aSet: TSet; aEnum: TEnum);
+begin
+ PByte(@aSet)[Integer(aEnum) shr 3] := PByte(@aSet)[Integer(aEnum) shr 3] and not (1 shl (Integer(aEnum) and 7));
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.ToString(const aValue: TSet; const aSeperator: String): String;
+var
+ e: TEnum;
+ i: Integer;
+ p: PByte;
+begin
+ result := '';
+ p := @aValue;
+ for e in TEnumHelper.Values do begin
+ i := Integer(e);
+ if IsSet(aValue, e) then begin
+ if result > '' then
+ result := result + aSeperator;
+ result := result + TEnumHelper.ToString(e);
+ end;
+ end;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.TryToSet(const aStr: String; out aValue: TSet): Boolean;
+begin
+ result := TryToSet(aStr, ',', aValue);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.TryToSet(const aStr: String; const aSeperator: String; out aValue: TSet): Boolean;
+var
+ i, j: Integer;
+ p: PAnsiChar;
+ s: String;
+ e: TEnum;
+begin
+ if (aSeperator = '') then
+ raise EArgumentException.Create('''aSeperator'' can not be empty');
+
+ result := true;
+ aValue := [];
+ i := 1;
+ j := 1;
+
+ while (i <= Length(aStr)) do begin
+ if (Copy(aStr, i, Length(aSeperator)) = aSeperator) then begin
+ s := Trim(copy(aStr, j, i - j));
+ if (s <> '') then begin
+ result := result and TEnumHelper.TryToEnum(s, e);
+ if not result then
+ exit;
+ SetValue(aValue, e);
+ j := i + Length(aSeperator);
+ end;
+ end;
+ inc(i);
+ end;
+
+ s := Trim(copy(aStr, j, i - j));
+ if (s <> '') then begin
+ result := result and TEnumHelper.TryToEnum(s, e);
+ if not result then
+ exit;
+ SetValue(aValue, e);
+ end
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.ToSet(const aStr: String; const aDefault: TSet): TSet;
+begin
+ if not TryToSet(aStr, result) then
+ result := aDefault;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.ToSet(const aStr: String): TSet;
+begin
+ if not TryToSet(aStr, result) then
+ raise EEnumConvertException.CreateFmt('"%s" is an invalid value', [aStr]);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class function TutlSetHelper.Compare(const aSet1, aSet2: TSet): Integer;
+var
+ e: TEnum;
+begin
+ result := 0;
+ for e in TEnumHelper.Values do begin
+ if IsSet(aSet1, e) and not IsSet(aSet2, e) then begin
+ result := 1;
+ break;
+ end else if not IsSet(aSet1, e) and IsSet(aSet2, e) then begin
+ result := -1;
+ break;
+ end;
+ end;
+end;
+
end.