| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- unit Unit1;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls, sdDB, sdProvider, DB, ADODB, ComCtrls;
- type
- TForm1 = class(TForm)
- mmError: TMemo;
- btnCheck: TButton;
- edtFile: TEdit;
- acTree: TADOConnection;
- sapTree: TsdADOProvider;
- sdsTree: TsdDataSet;
- ProgressBar: TProgressBar;
- OpenDialog: TOpenDialog;
- lblError: TLabel;
- btnRecheck: TButton;
- procedure btnCheckClick(Sender: TObject);
- procedure FormDestroy(Sender: TObject);
- procedure btnRecheckClick(Sender: TObject);
- private
- { Private declarations }
- FIdx: TsdIndex;
- procedure CheckTree;
- public
- { Public declarations }
- end;
- var
- Form1: TForm1;
- implementation
- uses
- ScUtils;
- {$R *.dfm}
- const
- SAdoConnectStr = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s;User ID=%s;Password=%s;Persist Security Info=True';
- procedure TForm1.btnCheckClick(Sender: TObject);
- begin
- if OpenDialog.Execute and FileExists(OpenDialog.FileName) then
- begin
- edtFile.Text := OpenDialog.FileName;
- Screen.Cursor := crHourGlass;
- try
- sdsTree.Close;
- acTree.Close;
- sdsTree.ClearIndex;
- acTree.ConnectionString := Format(SAdoConnectStr, [edtFile.Text, 'Admin', '']);
- acTree.Open;
- sapTree.TableName := 'Bills';
- sdsTree.Open;
- FIdx := sdsTree.AddIndex('idxParent', 'ParentID;SerialNo');
- mmError.Clear;
- CheckTree;
- sdsTree.Close;
- acTree.Close;
- finally
- Screen.Cursor := crDefault;
- end;
- end;
- end;
- procedure TForm1.btnRecheckClick(Sender: TObject);
- begin
- if not FileExists(edtFile.Text) then Exit;
- Screen.Cursor := crHourGlass;
- try
- sdsTree.ClearIndex;
- acTree.Open;
- sdsTree.Open;
- FIdx := sdsTree.AddIndex('idxParent', 'ParentID;SerialNo');
- mmError.Clear;
- CheckTree;
- sdsTree.Close;
- acTree.Close;
- finally
- Screen.Cursor := crDefault;
- end;
- end;
- procedure TForm1.CheckTree;
- var
- I, J, K, iError, iFirstChild, iEndlessNextID, iLength: Integer;
- // [[ID, NextSibingID], []...]
- arrChild: array of array of Integer;
- Rec1, Rec2, Rec3: TsdDataRecord;
- bOnlyOneChild, bHasNext, bHasMoreThanOneNext, bHasLastNode, bHasMoreThanOneLastNode, bIsFirstChild: Boolean;
- function FindEndlessNext(AID: Integer): Integer;
- var
- L, M, iID, iLastID: Integer;
- arrNext: array of Integer;
- bHasNext: Boolean;
- begin
- Result := -1;
- if Length(arrChild) <= 1 then Exit;
- iLastID := AID;
- repeat
- iID := iLastID;
- bHasNext := False;
- for L := Low(arrChild) to High(arrChild) do
- begin
- if arrChild[L, 0] = iID then
- begin
- iLastID := arrChild[L, 1];
- if ArrayIndexOf(iLastID, arrNext) > 0 then
- Exit;
- bHasNext := True;
- SetLength(arrNext, Length(arrNext) + 1);
- arrNext[Length(arrNext) - 1] := iLastID;
- Break;
- end;
- end;
- until (iLastID = AID) or (iLastID = -1) or not bHasNext;
- Result := iLastID;
- end;
- begin
- ProgressBar.Max := FIdx.RecordCount - 2;
- ProgressBar.Position := 0;
- bIsFirstChild := True;
- iError := 0;
- iFirstChild := 0;
- for I := 0 to FIdx.RecordCount - 2 do
- begin
- Rec1 := FIdx.Records[I];
- bOnlyOneChild := False;
- bHasNext := False;
- bHasMoreThanOneNext := False;
- bHasLastNode := False;
- bHasMoreThanOneLastNode := False;
- iEndlessNextID := -1;
- // 生产所有兄弟的ID关系数组,后面检查兄弟关系死循环用
- if bIsFirstChild then
- for K := iFirstChild to FIdx.RecordCount - 1 do
- begin
- Rec3 := FIdx.Records[K];
- // 是否换Parent
- if Rec1.ValueByName('ParentID').AsInteger <> Rec3.ValueByName('ParentID').AsInteger then Break;
- iLength := Length(arrChild) + 1;
- SetLength(arrChild, iLength, 2);
- arrChild[iLength - 1, 0] := Rec3.ValueByName('ID').AsInteger;
- arrChild[iLength - 1, 1] := Rec3.ValueByName('NextSiblingID').AsInteger;
- end;
- for J := iFirstChild to FIdx.RecordCount - 1 do
- begin
- ProgressBar.Position := I;
- Rec2 := FIdx.Records[J];
- // 判断是否到下一个父节点
- if Rec1.ValueByName('ParentID').AsInteger <> Rec2.ValueByName('ParentID').AsInteger then
- begin
- // 父节点只有一个儿子
- if J = I + 1 then
- begin
- bOnlyOneChild := True;
- if Rec1.ValueByName('NextSiblingID').AsInteger <> -1 then
- begin
- mmError.Lines.Add(Format('Node[ID: %d] has Only one Child and no LastChild.', [Rec1.ValueByName('ParentID').AsInteger]));
- Inc(iError);
- end;
- end;
- Break;
- end;
- // 检查最后兄弟
- if bIsFirstChild then
- begin
- if Rec2.ValueByName('NextSiblingID').AsInteger = -1 then
- begin
- if bHasLastNode then
- bHasMoreThanOneLastNode := True;
- bHasLastNode := True;
- end;
- end;
- // 寻找后兄弟
- if Rec1.ValueByName('NextSiblingID').AsInteger = Rec2.ValueByName('ID').AsInteger then
- begin
- if bHasNext then
- bHasMoreThanOneNext := True;
- bHasNext := True;
- // 检查兄弟关系死循环
- iEndlessNextID := FindEndlessNext(Rec1.ValueByName('ID').AsInteger);
- end;
- if Rec2.ValueByName('NextSiblingID').AsInteger = Rec1.ValueByName('ID').AsInteger then
- end;
- if not bOnlyOneChild then
- begin
- if not bHasNext then
- begin
- mmError.Lines.Add(Format('Node[ID: %d] has no NextSibling.', [Rec1.ValueByName('ID').AsInteger]));
- Inc(iError);
- end;
- if bHasMoreThanOneNext then
- begin
- mmError.Lines.Add(Format('Node[ID: %d] has more than one NextSibling.', [Rec1.ValueByName('ID').AsInteger]));
- Inc(iError);
- end;
- if bIsFirstChild and not bHasLastNode then
- begin
- mmError.Lines.Add(Format('Node[ID: %d] has no LastChild.', [Rec1.ValueByName('ParentID').AsInteger]));
- Inc(iError);
- end;
- if bHasMoreThanOneLastNode then
- begin
- mmError.Lines.Add(Format('Node[ID: %d] has more than one LastChld.', [Rec1.ValueByName('ParentID').AsInteger]));
- Inc(iError);
- end;
- if iEndlessNextID >= 0 then
- begin
- mmError.Lines.Add(Format('Node[ID: %d] have endless loop.', [Rec1.ValueByName('ID').AsInteger]));
- Inc(iError);
- end;
- end;
- Rec2 := FIdx.Records[I + 1];
- bIsFirstChild := Rec1.ValueByName('ParentID').AsInteger <> Rec2.ValueByName('ParentID').AsInteger;
- if bIsFirstChild then
- begin
- iFirstChild := I + 1;
- SetLength(arrChild, 0, 0);
- end;
- end;
- lblError.Caption := Format('共%d条记录,%d条错误', [sdsTree.RecordCount, iError]);
- end;
- procedure TForm1.FormDestroy(Sender: TObject);
- begin
- sdsTree.Close;
- acTree.Close;
- end;
- end.
|