|
@@ -29,8 +29,8 @@ interface
|
|
uses
|
|
uses
|
|
sysutils, math;
|
|
sysutils, math;
|
|
|
|
|
|
-Type TConvType = type Integer;
|
|
|
|
- TConvFamily = type Integer;
|
|
|
|
|
|
+Type TConvType = type Word;
|
|
|
|
+ TConvFamily = type Word;
|
|
TConvFamilyArray = array of TConvFamily;
|
|
TConvFamilyArray = array of TConvFamily;
|
|
TConvTypeArray = array of TConvType;
|
|
TConvTypeArray = array of TConvType;
|
|
TConversionProc = function(const AValue: Double): Double;
|
|
TConversionProc = function(const AValue: Double): Double;
|
|
@@ -38,8 +38,8 @@ Type TConvType = type Integer;
|
|
EConversionError = class(EConvertError);
|
|
EConversionError = class(EConvertError);
|
|
|
|
|
|
const
|
|
const
|
|
- CIllegalConvFamily = TConvFamily(0);
|
|
|
|
- CIllegalConvType = TConvType(0);
|
|
|
|
|
|
+ CIllegalConvFamily = TConvFamily(0); // Delphi compatible but makes no sense since 0 is a valid value for a TConvFamily
|
|
|
|
+ CIllegalConvType = TConvType(0); // Delphi compatible but makes no sense since 0 is a valid value for a TConvType
|
|
GConvUnitToStrFmt: string = '%f %s';
|
|
GConvUnitToStrFmt: string = '%f %s';
|
|
|
|
|
|
Function RegisterConversionFamily(Const S : String):TConvFamily;
|
|
Function RegisterConversionFamily(Const S : String):TConvFamily;
|
|
@@ -188,7 +188,7 @@ var
|
|
|
|
|
|
begin
|
|
begin
|
|
D:=Convert(AAmount, AAMountType, AType);
|
|
D:=Convert(AAmount, AAMountType, AType);
|
|
- //don't use InRange() since it does have an epsilon parameter
|
|
|
|
|
|
+ //don't use InRange() since it does not have an epsilon parameter
|
|
result:=(CompareValue(ATest,AValue,macheps)<>LessThanValue) and
|
|
result:=(CompareValue(ATest,AValue,macheps)<>LessThanValue) and
|
|
(CompareValue(ATest,AValue+D,macheps)<>GreaterThanValue);
|
|
(CompareValue(ATest,AValue+D,macheps)<>GreaterThanValue);
|
|
end;
|
|
end;
|
|
@@ -209,9 +209,10 @@ end;
|
|
function ConvFamilyToDescription(const AFamily: TConvFamily): string;
|
|
function ConvFamilyToDescription(const AFamily: TConvFamily): string;
|
|
|
|
|
|
begin
|
|
begin
|
|
- result:='';
|
|
|
|
if AFamily<length(TheFamilies) then
|
|
if AFamily<length(TheFamilies) then
|
|
- result:=TheFamilies[AFamily];
|
|
|
|
|
|
+ result:=TheFamilies[AFamily]
|
|
|
|
+ else
|
|
|
|
+ result:=format(SConvUnknownDescriptionWithPrefix,['$',AFamily]);
|
|
end;
|
|
end;
|
|
|
|
|
|
function ConvUnitToStr(const AValue: Double; const AType: TConvType): string;
|
|
function ConvUnitToStr(const AValue: Double; const AType: TConvType): string;
|
|
@@ -264,12 +265,21 @@ begin
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+//since a conversion type actually can have any (incuding an empty) description we need a function that
|
|
|
|
+//properly checks and indicates wether or not AType actually exists
|
|
|
|
+function TryConvTypeToDescription(const AType: TConvType; out S: string): Boolean;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ result:=AType<length(TheUnits);
|
|
|
|
+ if result then
|
|
|
|
+ S:=TheUnits[AType].Description;
|
|
|
|
+end;
|
|
|
|
+
|
|
function ConvTypeToDescription(const AType: TConvType): string;
|
|
function ConvTypeToDescription(const AType: TConvType): string;
|
|
|
|
|
|
Begin
|
|
Begin
|
|
- result:='';
|
|
|
|
- if AType<length(TheUnits) then
|
|
|
|
- result:=TheUnits[AType].Description;
|
|
|
|
|
|
+ if not TryConvTypeToDescription(AType, result) then
|
|
|
|
+ result:=format(SConvUnknownDescriptionWithPrefix,['$',AType]);
|
|
end;
|
|
end;
|
|
|
|
|
|
function DescriptionToConvType(const ADescription: String; out AType: TConvType): Boolean;
|
|
function DescriptionToConvType(const ADescription: String; out AType: TConvType): Boolean;
|
|
@@ -305,22 +315,44 @@ begin
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+//since a conversion family actually can have any (including an empty) description we need a function that
|
|
|
|
+//properly checks and indicates wether or not AType actually exists
|
|
|
|
+function TryConvTypeToFamily(const AType: TConvType; out AFam: TConvFamily): Boolean;
|
|
|
|
+
|
|
|
|
+var
|
|
|
|
+ Fam: TConvFamily;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ result:=false;
|
|
|
|
+ if AType<length(TheUnits) then begin
|
|
|
|
+ Fam:=TheUnits[AType].Fam;
|
|
|
|
+ result:=true;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
function ConvTypeToFamily(const AType: TConvType): TConvFamily;
|
|
function ConvTypeToFamily(const AType: TConvType): TConvFamily;
|
|
|
|
|
|
begin
|
|
begin
|
|
- result:=CIllegalConvFamily;
|
|
|
|
- if AType<length(TheUnits) then
|
|
|
|
- result:=TheUnits[AType].Fam;
|
|
|
|
|
|
+ //result:=CIllegalConvFamily; //Delphi Docs says it does this, but actually it raises an exception upon error
|
|
|
|
+ if not TryConvTypeToFamily(AType, result) then
|
|
|
|
+ raise EConversionError.CreateFmt(SConvUnknownType,[format(SConvUnknownDescriptionWithPrefix,['$',AType])]);
|
|
end;
|
|
end;
|
|
|
|
|
|
function ConvTypeToFamily(const AFrom, ATo: TConvType): TConvFamily;
|
|
function ConvTypeToFamily(const AFrom, ATo: TConvType): TConvFamily;
|
|
|
|
|
|
-begin
|
|
|
|
- result:=ConvTypeToFamily(AFrom);
|
|
|
|
- if result<>CIllegalConvFamily then begin
|
|
|
|
- if ConvTypeToFamily(ATo)<>result then
|
|
|
|
- result:=CIllegalConvFamily;
|
|
|
|
- end;
|
|
|
|
|
|
+var
|
|
|
|
+ AFromS, AToS: String;
|
|
|
|
+ Fam1, Fam2: TConvFamily;
|
|
|
|
+begin
|
|
|
|
+ // a bit convoluted but Delphi actually raises exceptions that use the descriptions of AFrom and ATo
|
|
|
|
+ AFromS:=ConvTypeToDescription(AFrom);
|
|
|
|
+ AToS:=ConvTypeToDescription(ATo);
|
|
|
|
+ if TryConvTypeToFamily(AFrom, Fam1) and
|
|
|
|
+ TryConvTypeToFamily(ATo, Fam2) and
|
|
|
|
+ (Fam1=Fam2) then
|
|
|
|
+ result:=Fam1
|
|
|
|
+ else
|
|
|
|
+ raise EConversionError.CreateFmt(SConvIncompatibleTypes2,[AFromS, AToS]);
|
|
end;
|
|
end;
|
|
|
|
|
|
function CompatibleConversionType(const AType: TConvType;
|
|
function CompatibleConversionType(const AType: TConvType;
|
|
@@ -333,7 +365,7 @@ end;
|
|
function CompatibleConversionTypes(const AFrom, ATo: TConvType): Boolean;
|
|
function CompatibleConversionTypes(const AFrom, ATo: TConvType): Boolean;
|
|
|
|
|
|
begin
|
|
begin
|
|
- //ConvTypeToFamily returns TConvFamily(0) if the TConvType is not registerd, but 0 is a valid value for a TConvFamily
|
|
|
|
|
|
+ //ConvTypeToFamily potentially raises an exception, make sure it doesn't here
|
|
result:= (AFrom<length(TheUnits)) and (ATo<length(TheUnits)) and
|
|
result:= (AFrom<length(TheUnits)) and (ATo<length(TheUnits)) and
|
|
(ConvTypeToFamily(AFrom)=ConvTypeToFamily(ATo));
|
|
(ConvTypeToFamily(AFrom)=ConvTypeToFamily(ATo));
|
|
end;
|
|
end;
|
|
@@ -352,6 +384,8 @@ begin
|
|
end
|
|
end
|
|
else
|
|
else
|
|
begin
|
|
begin
|
|
|
|
+ if l=Integer(High(TConvFamily)) then
|
|
|
|
+ raise EConversionError.CreateFmt(SConvTooManyConvFamilies,[High(TConvFamily)]);
|
|
i:=0;
|
|
i:=0;
|
|
while (i<l) and (s<>TheFamilies[i]) do inc(i);
|
|
while (i<l) and (s<>TheFamilies[i]) do inc(i);
|
|
if i=l Then
|
|
if i=l Then
|
|
@@ -378,9 +412,9 @@ var l1 : Longint;
|
|
begin
|
|
begin
|
|
If NOT CheckFamily(Fam) Then
|
|
If NOT CheckFamily(Fam) Then
|
|
raise EConversionError.CreateFmt(SConvUnknownFamily, [IntToStr(Fam)]);
|
|
raise EConversionError.CreateFmt(SConvUnknownFamily, [IntToStr(Fam)]);
|
|
- if (value+1.0)<macheps then // not properly defined yet.
|
|
|
|
- exit(-1);
|
|
|
|
l1:=length(theunits);
|
|
l1:=length(theunits);
|
|
|
|
+ if l1=Integer(High(TConvType)) then
|
|
|
|
+ raise EConversionError.CreateFmt(SConvTooManyConvTypes,[High(TConvType)]);
|
|
Setlength(theunits,l1+1);
|
|
Setlength(theunits,l1+1);
|
|
theunits[l1].description:=s;
|
|
theunits[l1].description:=s;
|
|
theunits[l1].value:=value;
|
|
theunits[l1].value:=value;
|