UPD: Move tree builder class

This commit is contained in:
Alexander Koblov 2015-07-04 07:33:04 +00:00
commit 368fbbd1d1
2 changed files with 278 additions and 239 deletions

View file

@ -11,8 +11,7 @@ uses
uFileSourceOperationOptions,
uFileSourceOperationUI,
uFileSourceCopyOperation,
uSearchTemplate,
uFindFiles;
uFileSourceTreeBuilder;
function ApplyRenameMask(aFile: TFile; NameMask: String; ExtMask: String): String; overload;
procedure FillAndCount(Files: TFiles;
@ -52,56 +51,10 @@ type
{ TFileSystemTreeBuilder }
TFileSystemTreeBuilder = class
private
FFilesTree: TFileTree;
FFilesCount: Int64;
FCurrentDepth: Integer;
FDirectoriesCount: Int64;
FFilesSize: Int64;
FExcludeRootDir: Boolean;
FFileTemplate: TSearchTemplate;
FExcludeEmptyTemplateDirectories: Boolean;
FSymlinkOption: TFileSourceOperationOptionSymLink;
FRecursive: Boolean;
FFileChecks: TFindFileChecks;
FRootDir: String;
AskQuestion: TAskQuestionFunction;
CheckOperationState: TCheckOperationStateFunction;
procedure AddItem(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddFilesInDirectory(srcPath: String; CurrentNode: TFileTreeNode);
procedure AddFile(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddLink(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddLinkTarget(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddDirectory(aFile: TFile; CurrentNode: TFileTreeNode);
procedure DecideOnLink(aFile: TFile; CurrentNode: TFileTreeNode);
function GetItemsCount: Int64;
public
constructor Create(AskQuestionFunction: TAskQuestionFunction;
CheckOperationStateFunction: TCheckOperationStateFunction);
destructor Destroy; override;
procedure BuildFromFiles(Files: TFiles);
function ReleaseTree: TFileTree;
property ExcludeRootDir: Boolean read FExcludeRootDir write FExcludeRootDir;
property Recursive: Boolean read FRecursive write FRecursive;
property SymLinkOption: TFileSourceOperationOptionSymLink read FSymlinkOption write FSymlinkOption;
property FilesTree: TFileTree read FFilesTree;
property FilesSize: Int64 read FFilesSize;
property FilesCount: Int64 read FFilesCount;
property DirectoriesCount: Int64 read FDirectoriesCount;
property ItemsCount: Int64 read GetItemsCount;
property ExcludeEmptyTemplateDirectories: Boolean read FExcludeEmptyTemplateDirectories write FExcludeEmptyTemplateDirectories;
{en
Does not take ownership of SearchTemplate and does not free it.
}
property SearchTemplate: TSearchTemplate read FFileTemplate write FFileTemplate;
TFileSystemTreeBuilder = class(TFileSourceTreeBuilder)
protected
procedure AddFilesInDirectory(srcPath: String; CurrentNode: TFileTreeNode); override;
procedure AddLinkTarget(aFile: TFile; CurrentNode: TFileTreeNode); override;
end;
{ TFileSystemOperationHelper }
@ -196,7 +149,7 @@ implementation
uses
uDebug, uOSUtils, DCStrUtils, FileUtil, uFindEx, DCClassesUtf8, uFileProcs, uLng,
uDCUtils, DCBasicTypes, uFileSource, uFileSystemFileSource, uFileProperty,
DCBasicTypes, uFileSource, uFileSystemFileSource, uFileProperty,
StrUtils, DCDateTimeUtils, uShowMsg, Forms;
function ApplyRenameMask(aFile: TFile; NameMask: String; ExtMask: String): String; overload;
@ -298,80 +251,6 @@ end;
// ----------------------------------------------------------------------------
constructor TFileSystemTreeBuilder.Create(AskQuestionFunction: TAskQuestionFunction;
CheckOperationStateFunction: TCheckOperationStateFunction);
begin
AskQuestion := AskQuestionFunction;
CheckOperationState := CheckOperationStateFunction;
FRecursive := True;
FSymlinkOption := fsooslNone;
end;
destructor TFileSystemTreeBuilder.Destroy;
begin
inherited Destroy;
FFilesTree.Free;
end;
procedure TFileSystemTreeBuilder.BuildFromFiles(Files: TFiles);
var
i: Integer;
begin
FreeAndNil(FFilesTree);
FFilesTree := TFileTreeNode.Create;
FFilesTree.Data := TFileTreeNodeData.Create;
FFilesSize := 0;
FFilesCount := 0;
FDirectoriesCount := 0;
FCurrentDepth := 0;
FRootDir := Files.Path;
if Assigned(FFileTemplate) then
SearchTemplateToFindFileChecks(FFileTemplate.SearchRecord, FFileChecks);
if ExcludeRootDir then
begin
for i := 0 to Files.Count - 1 do
if Files[i].IsDirectory then
AddFilesInDirectory(Files[i].FullPath + DirectorySeparator, FFilesTree);
end
else
begin
for i := 0 to Files.Count - 1 do
AddItem(Files[i].Clone, FFilesTree);
end;
end;
procedure TFileSystemTreeBuilder.AddFile(aFile: TFile; CurrentNode: TFileTreeNode);
var
AddedNode: TFileTreeNode;
AddedIndex: Integer;
begin
AddedIndex := CurrentNode.AddSubNode(aFile);
AddedNode := CurrentNode.SubNodes[AddedIndex];
AddedNode.Data := TFileTreeNodeData.Create;
Inc(FFilesCount);
FFilesSize:= FFilesSize + aFile.Size;
CheckOperationState;
end;
procedure TFileSystemTreeBuilder.AddLink(aFile: TFile; CurrentNode: TFileTreeNode);
var
AddedNode: TFileTreeNode;
AddedIndex: Integer;
begin
AddedIndex := CurrentNode.AddSubNode(aFile);
AddedNode := CurrentNode.SubNodes[AddedIndex];
AddedNode.Data := TFileTreeNodeData.Create;
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveLinks := True;
Inc(FFilesCount);
end;
procedure TFileSystemTreeBuilder.AddLinkTarget(aFile: TFile; CurrentNode: TFileTreeNode);
var
LinkedFilePath: String;
@ -409,107 +288,6 @@ begin
end;
end;
procedure TFileSystemTreeBuilder.AddDirectory(aFile: TFile; CurrentNode: TFileTreeNode);
var
AddedNode: TFileTreeNode;
AddedIndex: Integer;
NodeData: TFileTreeNodeData;
begin
AddedIndex := CurrentNode.AddSubNode(aFile);
AddedNode := CurrentNode.SubNodes[AddedIndex];
NodeData := TFileTreeNodeData.Create;
AddedNode.Data := NodeData;
Inc(FDirectoriesCount);
if FRecursive then
begin
if not Assigned(FFileTemplate) or
(FFileTemplate.SearchRecord.SearchDepth < 0) or
(FCurrentDepth <= FFileTemplate.SearchRecord.SearchDepth) then
begin
Inc(FCurrentDepth);
AddFilesInDirectory(aFile.FullPath + DirectorySeparator, AddedNode);
Dec(FCurrentDepth);
end;
if Assigned(FFileTemplate) and FExcludeEmptyTemplateDirectories and
(AddedNode.SubNodesCount = 0) then
begin
CurrentNode.RemoveSubNode(AddedIndex);
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveExclusions := True;
end
else
begin
// Propagate flags to parent.
if NodeData.SubnodesHaveLinks then
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveLinks := True;
if NodeData.SubnodesHaveExclusions then
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveExclusions := True;
end;
end;
end;
procedure TFileSystemTreeBuilder.DecideOnLink(aFile: TFile; CurrentNode: TFileTreeNode);
begin
case FSymLinkOption of
fsooslFollow:
AddLinkTarget(aFile, CurrentNode);
fsooslDontFollow:
AddLink(aFile, CurrentNode);
fsooslNone:
begin
case AskQuestion('', Format(rsMsgFollowSymlink, [aFile.Name]),
[fsourYes, fsourAll, fsourNo, fsourSkipAll],
fsourYes, fsourNo)
of
fsourYes:
AddLinkTarget(aFile, CurrentNode);
fsourAll:
begin
FSymLinkOption := fsooslFollow;
AddLinkTarget(aFile, CurrentNode);
end;
fsourNo:
AddLink(aFile, CurrentNode);
fsourSkipAll:
begin
FSymLinkOption := fsooslDontFollow;
AddLink(aFile, CurrentNode);
end;
else
raise Exception.Create('Invalid user response');
end;
end;
else
raise Exception.Create('Invalid symlink option');
end;
end;
procedure TFileSystemTreeBuilder.AddItem(aFile: TFile; CurrentNode: TFileTreeNode);
var
Matches: Boolean;
begin
if Assigned(FFileTemplate) then
begin
Matches := CheckFile(FFileTemplate.SearchRecord, FFileChecks, aFile);
if Matches and (AFile.IsDirectory or AFile.IsLinkToDirectory) then
Matches := CheckDirectoryNameRelative(FFileChecks, aFile.FullPath, FRootDir);
if not Matches then
begin
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveExclusions := True;
Exit;
end;
end;
if aFile.IsDirectory then
AddDirectory(aFile, CurrentNode)
else if aFile.IsLink then
DecideOnLink(aFile, CurrentNode)
else
AddFile(aFile, CurrentNode);
end;
procedure TFileSystemTreeBuilder.AddFilesInDirectory(
srcPath: String;
CurrentNode: TFileTreeNode);
@ -530,17 +308,6 @@ begin
FindCloseEx(sr);
end;
function TFileSystemTreeBuilder.ReleaseTree: TFileTree;
begin
Result := FFilesTree;
FFilesTree := nil;
end;
function TFileSystemTreeBuilder.GetItemsCount: Int64;
begin
Result := FilesCount + DirectoriesCount;
end;
// ----------------------------------------------------------------------------
constructor TFileSystemOperationHelper.Create(AskQuestionFunction: TAskQuestionFunction;

View file

@ -0,0 +1,272 @@
unit uFileSourceTreeBuilder;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFile,
uFileSourceOperation,
uFileSourceOperationOptions,
uFileSourceOperationUI,
uSearchTemplate,
uFindFiles;
type
// Additional data for the filesystem tree node.
TFileTreeNodeData = class
public
// True if any of the subnodes (recursively) are links.
SubnodesHaveLinks: Boolean;
// Whether directory or subdirectories have any elements that will not be copied/moved.
SubnodesHaveExclusions: Boolean;
end;
{ TFileSourceTreeBuilder }
TFileSourceTreeBuilder = class
protected
FFilesTree: TFileTree;
FFilesCount: Int64;
FCurrentDepth: Integer;
FDirectoriesCount: Int64;
FFilesSize: Int64;
FExcludeRootDir: Boolean;
FFileTemplate: TSearchTemplate;
FExcludeEmptyTemplateDirectories: Boolean;
FSymlinkOption: TFileSourceOperationOptionSymLink;
FRecursive: Boolean;
FFileChecks: TFindFileChecks;
FRootDir: String;
AskQuestion: TAskQuestionFunction;
CheckOperationState: TCheckOperationStateFunction;
procedure AddItem(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddFilesInDirectory(srcPath: String; CurrentNode: TFileTreeNode); virtual; abstract;
procedure AddFile(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddLink(aFile: TFile; CurrentNode: TFileTreeNode);
procedure AddLinkTarget(aFile: TFile; CurrentNode: TFileTreeNode); virtual; abstract;
procedure AddDirectory(aFile: TFile; CurrentNode: TFileTreeNode);
procedure DecideOnLink(aFile: TFile; CurrentNode: TFileTreeNode);
function GetItemsCount: Int64;
public
constructor Create(AskQuestionFunction: TAskQuestionFunction;
CheckOperationStateFunction: TCheckOperationStateFunction);
destructor Destroy; override;
procedure BuildFromFiles(Files: TFiles);
function ReleaseTree: TFileTree;
property ExcludeRootDir: Boolean read FExcludeRootDir write FExcludeRootDir;
property Recursive: Boolean read FRecursive write FRecursive;
property SymLinkOption: TFileSourceOperationOptionSymLink read FSymlinkOption write FSymlinkOption;
property FilesTree: TFileTree read FFilesTree;
property FilesSize: Int64 read FFilesSize;
property FilesCount: Int64 read FFilesCount;
property DirectoriesCount: Int64 read FDirectoriesCount;
property ItemsCount: Int64 read GetItemsCount;
property ExcludeEmptyTemplateDirectories: Boolean read FExcludeEmptyTemplateDirectories write FExcludeEmptyTemplateDirectories;
{en
Does not take ownership of SearchTemplate and does not free it.
}
property SearchTemplate: TSearchTemplate read FFileTemplate write FFileTemplate;
end;
implementation
uses
uGlobs, uLng;
constructor TFileSourceTreeBuilder.Create(AskQuestionFunction: TAskQuestionFunction;
CheckOperationStateFunction: TCheckOperationStateFunction);
begin
AskQuestion := AskQuestionFunction;
CheckOperationState := CheckOperationStateFunction;
FRecursive := True;
FSymlinkOption := fsooslNone;
end;
destructor TFileSourceTreeBuilder.Destroy;
begin
inherited Destroy;
FFilesTree.Free;
end;
procedure TFileSourceTreeBuilder.BuildFromFiles(Files: TFiles);
var
i: Integer;
begin
FreeAndNil(FFilesTree);
FFilesTree := TFileTreeNode.Create;
FFilesTree.Data := TFileTreeNodeData.Create;
FFilesSize := 0;
FFilesCount := 0;
FDirectoriesCount := 0;
FCurrentDepth := 0;
FRootDir := Files.Path;
if Assigned(FFileTemplate) then
SearchTemplateToFindFileChecks(FFileTemplate.SearchRecord, FFileChecks);
if ExcludeRootDir then
begin
for i := 0 to Files.Count - 1 do
if Files[i].IsDirectory then
AddFilesInDirectory(Files[i].FullPath + DirectorySeparator, FFilesTree);
end
else
begin
for i := 0 to Files.Count - 1 do
AddItem(Files[i].Clone, FFilesTree);
end;
end;
procedure TFileSourceTreeBuilder.AddFile(aFile: TFile; CurrentNode: TFileTreeNode);
var
AddedNode: TFileTreeNode;
AddedIndex: Integer;
begin
AddedIndex := CurrentNode.AddSubNode(aFile);
AddedNode := CurrentNode.SubNodes[AddedIndex];
AddedNode.Data := TFileTreeNodeData.Create;
Inc(FFilesCount);
FFilesSize:= FFilesSize + aFile.Size;
CheckOperationState;
end;
procedure TFileSourceTreeBuilder.AddLink(aFile: TFile; CurrentNode: TFileTreeNode);
var
AddedNode: TFileTreeNode;
AddedIndex: Integer;
begin
AddedIndex := CurrentNode.AddSubNode(aFile);
AddedNode := CurrentNode.SubNodes[AddedIndex];
AddedNode.Data := TFileTreeNodeData.Create;
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveLinks := True;
Inc(FFilesCount);
end;
procedure TFileSourceTreeBuilder.AddDirectory(aFile: TFile; CurrentNode: TFileTreeNode);
var
AddedNode: TFileTreeNode;
AddedIndex: Integer;
NodeData: TFileTreeNodeData;
begin
AddedIndex := CurrentNode.AddSubNode(aFile);
AddedNode := CurrentNode.SubNodes[AddedIndex];
NodeData := TFileTreeNodeData.Create;
AddedNode.Data := NodeData;
Inc(FDirectoriesCount);
if FRecursive then
begin
if not Assigned(FFileTemplate) or
(FFileTemplate.SearchRecord.SearchDepth < 0) or
(FCurrentDepth <= FFileTemplate.SearchRecord.SearchDepth) then
begin
Inc(FCurrentDepth);
AddFilesInDirectory(aFile.FullPath + DirectorySeparator, AddedNode);
Dec(FCurrentDepth);
end;
if Assigned(FFileTemplate) and FExcludeEmptyTemplateDirectories and
(AddedNode.SubNodesCount = 0) then
begin
CurrentNode.RemoveSubNode(AddedIndex);
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveExclusions := True;
end
else
begin
// Propagate flags to parent.
if NodeData.SubnodesHaveLinks then
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveLinks := True;
if NodeData.SubnodesHaveExclusions then
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveExclusions := True;
end;
end;
end;
procedure TFileSourceTreeBuilder.DecideOnLink(aFile: TFile; CurrentNode: TFileTreeNode);
begin
case FSymLinkOption of
fsooslFollow:
AddLinkTarget(aFile, CurrentNode);
fsooslDontFollow:
AddLink(aFile, CurrentNode);
fsooslNone:
begin
case AskQuestion('', Format(rsMsgFollowSymlink, [aFile.Name]),
[fsourYes, fsourAll, fsourNo, fsourSkipAll],
fsourYes, fsourNo)
of
fsourYes:
AddLinkTarget(aFile, CurrentNode);
fsourAll:
begin
FSymLinkOption := fsooslFollow;
AddLinkTarget(aFile, CurrentNode);
end;
fsourNo:
AddLink(aFile, CurrentNode);
fsourSkipAll:
begin
FSymLinkOption := fsooslDontFollow;
AddLink(aFile, CurrentNode);
end;
else
raise Exception.Create('Invalid user response');
end;
end;
else
raise Exception.Create('Invalid symlink option');
end;
end;
procedure TFileSourceTreeBuilder.AddItem(aFile: TFile; CurrentNode: TFileTreeNode);
var
Matches: Boolean;
begin
if Assigned(FFileTemplate) then
begin
Matches := CheckFile(FFileTemplate.SearchRecord, FFileChecks, aFile);
if Matches and (AFile.IsDirectory or AFile.IsLinkToDirectory) then
Matches := CheckDirectoryNameRelative(FFileChecks, aFile.FullPath, FRootDir);
if not Matches then
begin
(CurrentNode.Data as TFileTreeNodeData).SubnodesHaveExclusions := True;
Exit;
end;
end;
if aFile.IsDirectory then
AddDirectory(aFile, CurrentNode)
else if aFile.IsLink then
DecideOnLink(aFile, CurrentNode)
else
AddFile(aFile, CurrentNode);
end;
function TFileSourceTreeBuilder.ReleaseTree: TFileTree;
begin
Result := FFilesTree;
FFilesTree := nil;
end;
function TFileSourceTreeBuilder.GetItemsCount: Int64;
begin
Result := FilesCount + DirectoriesCount;
end;
end.