* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "io/dev/file_monitor.h"
#include <algorithm>
#include <cstddef>
#include <base/containers/iterator.h>
#include <base/containers/string.h>
#include <base/containers/string_view.h>
#include <base/containers/type_traits.h>
#include <base/containers/unique_ptr.h>
#include <base/containers/unordered_map.h>
#include <base/containers/vector.h>
#include <base/namespace.h>
#include <core/io/intf_directory.h>
#include <core/io/intf_file_manager.h>
#include <core/namespace.h>
#include <core/perf/cpu_perf_scope.h>
CORE_BEGIN_NAMESPACE()
using BASE_NS::string;
using BASE_NS::string_view;
using BASE_NS::vector;
void FileMonitor::RecursivelyCollectAllFiles(string& path)
{
auto dir = fileManager_.OpenDirectory(path);
if (dir == nullptr) {
return;
}
const auto& entries = dir->GetEntries();
const size_t oldLength = path.length();
for (const IDirectory::Entry& entry : entries) {
if (entry.name == "." || entry.name == "..") {
continue;
}
path.reserve(oldLength + entry.name.length() + 1);
path += entry.name;
if (entry.type == IDirectory::Entry::DIRECTORY) {
path += '/';
}
auto iterator = files_.find(path);
if (iterator != files_.end()) {
if (entry.timestamp == iterator->second.timestamp) {
iterator->second.state = FileInfo::NOCHANGE;
} else {
iterator->second.timestamp = entry.timestamp;
iterator->second.state = FileInfo::MODIFIED;
}
} else {
files_.insert({path, {entry.timestamp, FileInfo::ADDED}});
}
if (entry.type == IDirectory::Entry::DIRECTORY) {
RecursivelyCollectAllFiles(path);
}
path.resize(oldLength);
}
}
FileMonitor::FileMonitor(IFileManager& manager) : fileManager_(manager)
{}
void FileMonitor::CleanPath(const string_view inPath, string& path)
{
if ((inPath.back() != '/') && (inPath.back() != '\\')) {
path.reserve(inPath.size() + 1);
path = inPath;
path += '/';
} else {
path = inPath;
}
for (auto& c : path) {
if (c == '\\') {
c = '/';
}
}
}
bool FileMonitor::AddPath(const string_view inPath)
{
string path;
CleanPath(inPath, path);
if (IsWatchingDirectory(path) || IsWatchingSubDirectory(path)) {
return false;
}
RecursivelyCollectAllFiles(path);
if (path.capacity() > pathTmp_.capacity()) {
pathTmp_.reserve(path.capacity());
}
for (auto& ref : files_) {
ref.second.state = FileInfo::REMOVED;
}
directories_.push_back(path);
return true;
}
bool FileMonitor::RemovePath(const string_view inPath)
{
string path;
CleanPath(inPath, path);
const auto iterator = std::find(directories_.cbegin(), directories_.cend(), path);
if (iterator == directories_.cend()) {
return false;
}
for (auto it = files_.begin(); it != files_.end();) {
if (it->first.starts_with(path)) {
it = files_.erase(it);
} else {
++it;
}
}
directories_.erase(iterator);
return true;
}
bool FileMonitor::IsWatchingDirectory(const string_view inPath)
{
string path;
CleanPath(inPath, path);
for (const auto& ref : directories_) {
if (path.find(ref) != string_view::npos) {
return true;
}
}
return false;
}
bool FileMonitor::IsWatchingSubDirectory(const string_view inPath)
{
string path;
CleanPath(inPath, path);
for (const auto& ref : directories_) {
if (ref.find(path) != string_view::npos) {
return true;
}
}
return false;
}
void FileMonitor::ScanModifications(vector<string>& added, vector<string>& removed, vector<string>& modified)
{
CORE_CPU_PERF_SCOPE("CORE", "FileMonitor", "ScanModifications()", CORE_PROFILER_DEFAULT_COLOR);
for (const auto& ref : directories_) {
pathTmp_ = ref;
RecursivelyCollectAllFiles(pathTmp_);
}
for (auto it = files_.begin(); it != files_.end();) {
if (it->second.state == FileInfo::REMOVED) {
removed.push_back(it->first);
} else if (it->second.state == FileInfo::MODIFIED) {
modified.push_back(it->first);
} else if (it->second.state == FileInfo::ADDED) {
added.push_back(it->first);
}
if (it->second.state != FileInfo::REMOVED) {
it->second.state = FileInfo::REMOVED;
++it;
} else {
it = files_.erase(it);
}
}
}
vector<string> FileMonitor::GetMonitoredFiles() const
{
vector<string> filesRes;
filesRes.reserve(files_.size());
for (auto& f : files_) {
filesRes.push_back(f.first);
}
return filesRes;
}
CORE_END_NAMESPACE()