From cd2e076da1f6c38ebffb5d7ed7e55707b73b4cdf Mon Sep 17 00:00:00 2001 From: wp Date: Sun, 24 Feb 2019 21:27:39 +0000 Subject: [PATCH] TAChart: Fix TListChartSource.DataPoints streaming error. Issue #0035125, patch by Marcin Wiazowski. Update test program. git-svn-id: trunk@60490 - --- .../tachart/languages/tachartstrconsts.de.po | 4 + .../tachart/languages/tachartstrconsts.fi.po | 4 + .../tachart/languages/tachartstrconsts.fr.po | 4 + .../tachart/languages/tachartstrconsts.hu.po | 4 + .../tachart/languages/tachartstrconsts.lt.po | 4 + .../tachart/languages/tachartstrconsts.pl.po | 4 + .../tachart/languages/tachartstrconsts.pot | 4 + .../languages/tachartstrconsts.pt_BR.po | 4 + .../tachart/languages/tachartstrconsts.ru.po | 4 + .../tachart/languages/tachartstrconsts.se.po | 4 + .../tachart/languages/tachartstrconsts.uk.po | 4 + .../languages/tachartstrconsts.zh_CN.po | 4 + components/tachart/tachartstrconsts.pas | 3 + components/tachart/tacustomsource.pas | 1 + components/tachart/tasources.pas | 101 ++++++++++++++---- components/tachart/test/SourcesTest.pas | 77 ++++++++++++- components/tachart/test/test.lpi | 8 +- 17 files changed, 214 insertions(+), 24 deletions(-) diff --git a/components/tachart/languages/tachartstrconsts.de.po b/components/tachart/languages/tachartstrconsts.de.po index 86c4b38c28..e8aa939051 100644 --- a/components/tachart/languages/tachartstrconsts.de.po +++ b/components/tachart/languages/tachartstrconsts.de.po @@ -290,6 +290,10 @@ msgstr "Linear" msgid "Line series" msgstr "Linien-Diagramm" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "Die Anzahl der Datenwerte im %s.DataPoints-String \"%s\" entspricht nicht der, die aufgrund von XCount und YCount erwartet wird." + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logarithmisch" diff --git a/components/tachart/languages/tachartstrconsts.fi.po b/components/tachart/languages/tachartstrconsts.fi.po index b6a2e29f8a..941769a8d9 100644 --- a/components/tachart/languages/tachartstrconsts.fi.po +++ b/components/tachart/languages/tachartstrconsts.fi.po @@ -278,6 +278,10 @@ msgstr "Lineaarinen" msgid "Line series" msgstr "Viivakuvaaja" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logaritminen" diff --git a/components/tachart/languages/tachartstrconsts.fr.po b/components/tachart/languages/tachartstrconsts.fr.po index 0d3bb23458..e386ba854f 100644 --- a/components/tachart/languages/tachartstrconsts.fr.po +++ b/components/tachart/languages/tachartstrconsts.fr.po @@ -288,6 +288,10 @@ msgstr "Linéaire" msgid "Line series" msgstr "Séries de lignes" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logarithmique" diff --git a/components/tachart/languages/tachartstrconsts.hu.po b/components/tachart/languages/tachartstrconsts.hu.po index 079e69b6d2..7b92b19cf4 100644 --- a/components/tachart/languages/tachartstrconsts.hu.po +++ b/components/tachart/languages/tachartstrconsts.hu.po @@ -288,6 +288,10 @@ msgstr "Lineáris" msgid "Line series" msgstr "Vonal" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logaritmikus" diff --git a/components/tachart/languages/tachartstrconsts.lt.po b/components/tachart/languages/tachartstrconsts.lt.po index db2a1dba84..1d0b8efcd6 100644 --- a/components/tachart/languages/tachartstrconsts.lt.po +++ b/components/tachart/languages/tachartstrconsts.lt.po @@ -289,6 +289,10 @@ msgstr "Tiesinė" msgid "Line series" msgstr "Linijų sekos" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logaritminė" diff --git a/components/tachart/languages/tachartstrconsts.pl.po b/components/tachart/languages/tachartstrconsts.pl.po index 652d49bf3c..2290c8fbd3 100644 --- a/components/tachart/languages/tachartstrconsts.pl.po +++ b/components/tachart/languages/tachartstrconsts.pl.po @@ -288,6 +288,10 @@ msgstr "Liniowy" msgid "Line series" msgstr "" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logarytmiczny" diff --git a/components/tachart/languages/tachartstrconsts.pot b/components/tachart/languages/tachartstrconsts.pot index 3447d274d4..2539aa6343 100644 --- a/components/tachart/languages/tachartstrconsts.pot +++ b/components/tachart/languages/tachartstrconsts.pot @@ -278,6 +278,10 @@ msgstr "" msgid "Line series" msgstr "" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "" diff --git a/components/tachart/languages/tachartstrconsts.pt_BR.po b/components/tachart/languages/tachartstrconsts.pt_BR.po index 37429af2a3..7a67b7b49a 100644 --- a/components/tachart/languages/tachartstrconsts.pt_BR.po +++ b/components/tachart/languages/tachartstrconsts.pt_BR.po @@ -288,6 +288,10 @@ msgstr "Linear" msgid "Line series" msgstr "Série Linha" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logarítimo" diff --git a/components/tachart/languages/tachartstrconsts.ru.po b/components/tachart/languages/tachartstrconsts.ru.po index ae8cec5aac..605960b4ad 100644 --- a/components/tachart/languages/tachartstrconsts.ru.po +++ b/components/tachart/languages/tachartstrconsts.ru.po @@ -288,6 +288,10 @@ msgstr "Линейный масштаб" msgid "Line series" msgstr "График" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Логарифмический масштаб" diff --git a/components/tachart/languages/tachartstrconsts.se.po b/components/tachart/languages/tachartstrconsts.se.po index b160bfb898..4a99c49674 100644 --- a/components/tachart/languages/tachartstrconsts.se.po +++ b/components/tachart/languages/tachartstrconsts.se.po @@ -291,6 +291,10 @@ msgstr "Linjär" msgid "Line series" msgstr "Linjediagram" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Logaritmisk" diff --git a/components/tachart/languages/tachartstrconsts.uk.po b/components/tachart/languages/tachartstrconsts.uk.po index 2e77b764d3..4d065c3c20 100644 --- a/components/tachart/languages/tachartstrconsts.uk.po +++ b/components/tachart/languages/tachartstrconsts.uk.po @@ -291,6 +291,10 @@ msgstr "Лінійний масштаб" msgid "Line series" msgstr "Графік" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "Логарифмічний масштаб" diff --git a/components/tachart/languages/tachartstrconsts.zh_CN.po b/components/tachart/languages/tachartstrconsts.zh_CN.po index 781bd7f096..fabd190f12 100644 --- a/components/tachart/languages/tachartstrconsts.zh_CN.po +++ b/components/tachart/languages/tachartstrconsts.zh_CN.po @@ -289,6 +289,10 @@ msgstr "线性的(Linear)" msgid "Line series" msgstr "折线图系列" +#: tachartstrconsts.rslistsourcestringformaterror +msgid "The data value count in the %0:s.DataPoints string \"%1:s\" differs from what is expected from XCount and YCount." +msgstr "" + #: tachartstrconsts.rslogarithmic msgid "Logarithmic" msgstr "对数的" diff --git a/components/tachart/tachartstrconsts.pas b/components/tachart/tachartstrconsts.pas index d7ccd5fc34..a618be911c 100644 --- a/components/tachart/tachartstrconsts.pas +++ b/components/tachart/tachartstrconsts.pas @@ -71,6 +71,9 @@ resourcestring // Chart sources rsSourceCountError = '%0:s requires a chart source with at least %1:d %2:s value(s) per data point.'; rsSourceCountError2 = 'This %0:s instance must have at least %1:d %2:s value(s) per data point.'; + rsListSourceStringFormatError = 'The data value count in the %0:s.DataPoints '+ + 'string "%1:s" differs from what is expected from XCount and YCount.'; + // Transformations tasAxisTransformsEditorTitle = 'Edit axis transformations'; diff --git a/components/tachart/tacustomsource.pas b/components/tachart/tacustomsource.pas index 2782948b84..1658662ef3 100644 --- a/components/tachart/tacustomsource.pas +++ b/components/tachart/tacustomsource.pas @@ -73,6 +73,7 @@ type EEditableSourceRequired = class(EChartError); EXCountError = class(EChartError); EYCountError = class(EChartError); + EListSourceStringFormatError = class(EChartError); TChartValueText = record FText: String; diff --git a/components/tachart/tasources.pas b/components/tachart/tasources.pas index 061a7332d6..b41fb8c2db 100644 --- a/components/tachart/tasources.pas +++ b/components/tachart/tasources.pas @@ -38,6 +38,7 @@ type protected function GetCount: Integer; override; function GetItem(AIndex: Integer): PChartDataItem; override; + procedure Loaded; override; procedure SetXCount(AValue: Cardinal); override; procedure SetYCount(AValue: Cardinal); override; public @@ -258,7 +259,10 @@ type TListChartSourceStrings = class(TStrings) strict private FSource: TListChartSource; + FLoadingCache: TStringList; procedure Parse(AString: String; ADataItem: PChartDataItem); + private + procedure LoadingFinished; protected function Get(Index: Integer): String; override; function GetCount: Integer; override; @@ -266,6 +270,7 @@ type procedure SetUpdateState(AUpdating: Boolean); override; public constructor Create(ASource: TListChartSource); + destructor Destroy; override; procedure Clear; override; procedure Delete(Index: Integer); override; procedure Insert(Index: Integer; const S: String); override; @@ -284,7 +289,10 @@ end; procedure TListChartSourceStrings.Clear; begin - FSource.Clear; + if not (csLoading in FSource.ComponentState) then + FSource.Clear + else + FreeAndNil(FLoadingCache); end; constructor TListChartSourceStrings.Create(ASource: TListChartSource); @@ -293,6 +301,12 @@ begin FSource := ASource; end; +destructor TListChartSourceStrings.Destroy; +begin + inherited; + FLoadingCache.Free; +end; + procedure TListChartSourceStrings.Delete(Index: Integer); begin FSource.Delete(Index); @@ -306,29 +320,49 @@ begin fs := DefaultFormatSettings; fs.DecimalSeparator := '.'; with FSource[Index]^ do begin - Result := Format('%g', [X], fs); + Result := ''; + if FSource.XCount > 0 then + Result += Format('%g|', [X], fs); for i := 0 to High(XList) do - Result += Format('|%g', [XList[i]], fs); + Result += Format('%g|', [XList[i]], fs); if FSource.YCount > 0 then - Result += Format('|%g', [Y], fs); + Result += Format('%g|', [Y], fs); for i := 0 to High(YList) do - Result += Format('|%g', [YList[i]], fs); - Result += Format('|%s|%s', [IntToColorHex(Color), Text]); + Result += Format('%g|', [YList[i]], fs); + Result += Format('%s|%s', [IntToColorHex(Color), Text]); end; end; function TListChartSourceStrings.GetCount: Integer; begin - Result := FSource.Count; + if not (csLoading in FSource.ComponentState) then + Result := FSource.Count + else + if Assigned(FLoadingCache) then + Result := FLoadingCache.Count + else + Result := 0; end; procedure TListChartSourceStrings.Insert(Index: Integer; const S: String); var item: PChartDataItem; begin + if csLoading in FSource.ComponentState then begin + if not Assigned(FLoadingCache) then + FLoadingCache := TStringList.Create; + FLoadingCache.Insert(Index, S); + exit; + end; + item := FSource.NewItem; - FSource.FData.Insert(Index, item); - Parse(S, item); + try + Parse(S, item); + FSource.FData.Insert(Index, item); + except + Dispose(item); + raise; + end; FSource.UpdateCachesAfterAdd(item^.X, item^.Y); end; @@ -350,20 +384,33 @@ var var i: Integer; begin + // Note: this method is called only when component loading is fully finished - + // so FSource.XCount and FSource.YCount are already properly estabilished + parts := Split(AString); try - if (FSource.XCount = 1) and (FSource.YCount + 3 < Cardinal(parts.Count)) then - FSource.YCount := parts.Count - 3; + // There should be XCount + YCount .. XCount + YCount + 2 (for Color and Text) + // parts of the string + if (Cardinal(parts.Count) <> FSource.XCount + FSource.YCount + 2) then begin + if Length(AString) > 20 then AString := Copy(AString, 1, 20) + '...'; + raise EListSourceStringFormatError.CreateFmt( + rsListSourceStringFormatError, + [IfThen(FSource.Name <> '', FSource.Name, FSource.ClassName), AString]); + end; + with ADataItem^ do begin - X := StrToFloatOrDateTimeDef(NextPart); - if FSource.XCount > 1 then + if FSource.XCount > 0 then begin + X := StrToFloatOrDateTimeDef(NextPart); for i := 0 to High(XList) do XList[i] := StrToFloatOrDateTimeDef(NextPart); + end else + X := NaN; if FSource.YCount > 0 then begin Y := StrToFloatOrDateTimeDef(NextPart); for i := 0 to High(YList) do YList[i] := StrToFloatOrDateTimeDef(NextPart); - end; + end else + Y := NaN; Color := StrToIntDef(NextPart, clTAColor); Text := NextPart; end; @@ -384,10 +431,22 @@ end; procedure TListChartSourceStrings.SetUpdateState(AUpdating: Boolean); begin - if AUpdating then - FSource.BeginUpdate - else - FSource.EndUpdate; + if not (csLoading in FSource.ComponentState) then + if AUpdating then + FSource.BeginUpdate + else + FSource.EndUpdate; +end; + +procedure TListChartSourceStrings.LoadingFinished; +begin + // csLoading in FSource.ComponentState is already cleared + if Assigned(FLoadingCache) then + try + Assign(FLoadingCache); + finally + FreeAndNil(FLoadingCache); + end; end; { TListChartSource } @@ -781,6 +840,12 @@ begin Notify; end; +procedure TListChartSource.Loaded; +begin + inherited; // clears csLoading in ComponentState + (FDataPoints as TListChartSourceStrings).LoadingFinished; +end; + { TMWCRandomGenerator } function TMWCRandomGenerator.Get: LongWord; diff --git a/components/tachart/test/SourcesTest.pas b/components/tachart/test/SourcesTest.pas index 42f60ea475..fdab6a903e 100644 --- a/components/tachart/test/SourcesTest.pas +++ b/components/tachart/test/SourcesTest.pas @@ -369,9 +369,9 @@ begin oldSeparator := DefaultFormatSettings.DecimalSeparator; try DefaultFormatSettings.DecimalSeparator := ':'; - FSource.DataPoints.Add('3:5'); + FSource.DataPoints.Add('3:5|?|?|'); AssertEquals(3.5, FSource[0]^.X); - FSource.DataPoints[0] := '4.5'; + FSource.DataPoints[0] := '4.5|?|?|'; AssertEquals(4.5, FSource[0]^.X); finally DefaultFormatSettings.DecimalSeparator := oldSeparator; @@ -453,9 +453,81 @@ procedure TListSourceTest.Multi; begin FSource.Clear; AssertEquals(1, FSource.YCount); + AssertEquals(1, FSource.YCount); + FSource.Add(1, 2); FSource.YCount := 2; AssertEquals([0], FSource[0]^.YList); + + FSource.SetYList(0, [3]); + AssertEquals(3, FSource[0]^.YList[0]); + + FSource.DataPoints.Add('1|2|3|?|t'); + AssertEquals(1, FSource.XCount); + AssertEquals(2, FSource.YCount); + AssertEquals(1, FSource[1]^.X); + AssertEquals(2, FSource[1]^.Y); + AssertEquals(3, FSource[1]^.YList[0]); + + try + FSource.DataPoints.Add('10|20|30|40|?|'); + except + on E: Exception do + AssertTrue('Too many values', E is EListSourceStringFormatError); + end; + AssertEquals(2, FSource.Count); + + try + FSource.DataPoints.Add('10|20|?|'); + except + on E: Exception do + AssertTrue('Too few values', E is EListSourceStringFormatError); + end; + AssertEquals(2, FSource.Count); + + try + FSource.DataPoints.Add('10|20|30|?'); + except + on E: Exception do + AssertTrue('Text field missing', E is EListSourceStringFormatError); + end; + AssertEquals(2, FSource.Count); + + try + FSource.DataPoints.Add('10|20|30|t'); + except + on E: Exception do + AssertTrue('Color field missing', E is EListSourceStringFormatError); + end; + AssertEquals(2, FSource.Count); + + try + FSource.AddXYList(4, []); + except + on E: Exception do + AssertTrue('Empty YList', E is TListChartSource.EYListEmptyError); + end; + + FSource.Clear; + FSource.XCount := 2; + FSource.YCount := 3; + FSource.AddXListYList([1, 2], [3, 4, 5]); + AssertEquals(2, FSource.XCount); + AssertEquals(3, FSource.YCount); + AssertEquals(1, FSource[0]^.X); + AssertEquals(2, FSource[0]^.XList[0]); + AssertEquals(3, FSource[0]^.Y); + AssertEquals(4, FSource[0]^.YList[0]); + AssertEquals(5, FSource[0]^.YList[1]); + + FSource.DataPoints.Add('10|20|30|40|50|?|t'); + AssertEquals(10, FSource[1]^.X); + AssertEquals(20, FSource[1]^.XList[0]); + AssertEquals(30, FSource[1]^.Y); + AssertEquals(40, FSource[1]^.YList[0]); + AssertEquals(50, FSource[1]^.YList[1]); + + (* FSource.SetYList(0, [3, 4]); AssertEquals('Extra items are chopped', [3], FSource[0]^.YList); FSource.DataPoints.Add('1|2|3|4|?|t'); @@ -478,6 +550,7 @@ begin except on E: Exception do AssertTrue('Empty YList', E is TListChartSource.EYListEmptyError); end; + *) end; procedure TListSourceTest.SetUp; diff --git a/components/tachart/test/test.lpi b/components/tachart/test/test.lpi index b0007fe33e..9ee1a8896b 100644 --- a/components/tachart/test/test.lpi +++ b/components/tachart/test/test.lpi @@ -24,9 +24,6 @@ - - - @@ -85,7 +82,7 @@ - + @@ -98,6 +95,9 @@ + + +