inpuPath a former member of class FileBot. I decided to removed this member variable in the recent design for
constructChildPath(). There are 5 test cases for this design specification - capturing files, 2 of the test case were failed, which is test case 4 and 5.
- one file is captured without sub folder.
- two files are captured without sub folder.
- one files are captured when there is a sub folder.
- one files are captured in directory under test and one file in sub folder.
- zero file are captured in directory under test and one file in sub folder.
The root cause for this failure was due to the misused of
inputPath as reference point in
constructChildPath().
void FileBot::constructChildPath()
{
vector<path> pathList;
// where am I now?
BOOST_LOG_TRIVIAL(info) << "current path: " << inputPath << inputPath.filename() << endl;
// capture the paths/files list under the source
copy(filesystem::directory_iterator(inputPath), filesystem::directory_iterator(), back_inserter(pathList));
...
...
}
For this purpose, I decide to remove this member variable to make
constructChildPath() more robust in processing file directory.
To do this, the first make over is to build a method to construct the path to feed
directory_iterator().
string FileBot::constructPathAddress(FileNode *node)
{
if( root == nullptr )
return "";
string filePath = "";
if( node->getParentNode() != nullptr )
filePath = constructPathAddress(node->getParentNode());
return filePath + node->getName().string() + string(1, boost::filesystem::path::preferred_separator);
}
Unlike the previous version, the
constructChildPath() has now been transformed into a recursive function. Take note on the
inputPath is getting the outcome from
constructPathAddress() and then feed into
directory_iterator().
void FileBot::constructChildPath(FileNode *currentRootNode)
{
vector<path> pathList;
// construct path address
string inputPath = constructPathAddress(currentRootNode);
// capture the paths/files list under the source
copy(filesystem::directory_iterator(inputPath), filesystem::directory_iterator(), back_inserter(pathList));
std::sort(pathList.begin(), pathList.end());
// scan through path and those files sit in that path
// scan for files in sub directory if there is any sub directory in that path
for(vector<path>::const_iterator it(pathList.begin()); it != pathList.end(); ++it) {
string file = FileHelper::convertPathToString((*it));
if( is_directory(file) ) {
FileNode *node = new FileNode();
node->setName(file.substr(file.find_last_of(boost::filesystem::path::preferred_separator) + 1, file.length()));
node->setType('d');
node->setParentNode(currentRootNode);
currentRootNode->sibling.push_back(*node);
BOOST_LOG_TRIVIAL(info) << "subFolderName : " << node->getName().string() << " : " << node;
constructChildPath(node);
}
else {
FileNode *node = new FileNode();
node->setName(file.substr(file.find_last_of(boost::filesystem::path::preferred_separator) + 1, file.length()));
node->setType('f');
node->setParentNode(currentRootNode);
currentRootNode->sibling.push_back(*node);
}
}
}
Last is to reassign the task of constructor to
validatePath(). I just felt that this task doesn't suitable to put into the constructor since it only use by
constructParentPath().
path FileBot::validatePath(boost::filesystem::path filePath)
{
// remove trailing file separator if found any
char lastChar = filePath.generic_string().at(filePath.generic_string().length() - 1);
if( lastChar == '/' )
filePath = filePath.generic_string().substr(0, filePath.generic_string().length() - 1);
return filePath;
}
Then in
constructParentPath() just call this method to update the input value again.
FileNode* FileBot::constructParentPath(boost::filesystem::path inputPath)
{
inputPath = validatePath(inputPath);
// bail out if the path doesn't exists
if( !exists(inputPath.generic_string()) )
return nullptr;
...
...
}