1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "file_resource_manager.h"
17
18 #include <iomanip>
19 #include <sstream>
20
21 #include <base/util/base64_decode.h>
22 #include <base/util/base64_encode.h>
23 #include <core/io/intf_filesystem_api.h>
24
25 #include <meta/interface/resource/intf_resource.h>
26
27 #include "memfile.h"
28
META_BEGIN_NAMESPACE()29 META_BEGIN_NAMESPACE()
30
31 static bool ResourceMatches(
32 const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection, const CORE_NS::ResourceId& id)
33 {
34 for (auto&& s : selection) {
35 if ((s.hasWildCardName || id.name == s.name) && (s.hasWildCardGroup || id.group == s.group)) {
36 return true;
37 }
38 }
39 return false;
40 }
41
FileResourceManager()42 FileResourceManager::FileResourceManager()
43 {
44 if (auto factory = CORE_NS::GetInstance<CORE_NS::IFileSystemApi>(CORE_NS::UID_FILESYSTEM_API_FACTORY)) {
45 fileManager_ = factory->CreateFilemanager();
46 fileManager_->RegisterFilesystem("file", factory->CreateStdFileSystem());
47 }
48 }
49
AddListener(const IResourceListener::Ptr & p)50 void FileResourceManager::AddListener(const IResourceListener::Ptr& p)
51 {
52 std::unique_lock lock { listenerMutex_ };
53 for (auto&& v : listeners_) {
54 if (v.lock() == p) {
55 return;
56 }
57 }
58 listeners_.push_back(p);
59 }
RemoveListener(const IResourceListener::Ptr & p)60 void FileResourceManager::RemoveListener(const IResourceListener::Ptr& p)
61 {
62 std::unique_lock lock { listenerMutex_ };
63 auto it = listeners_.begin();
64 for (; it != listeners_.end() && it->lock() != p; ++it) {
65 }
66 if (it != listeners_.end()) {
67 listeners_.erase(it);
68 }
69 }
Notify(const BASE_NS::vector<CORE_NS::ResourceId> & ids,IResourceListener::EventType type)70 void FileResourceManager::Notify(const BASE_NS::vector<CORE_NS::ResourceId>& ids, IResourceListener::EventType type)
71 {
72 if (!ids.empty()) {
73 BASE_NS::vector<IResourceListener::WeakPtr> list;
74 {
75 std::shared_lock lock { listenerMutex_ };
76 list = listeners_;
77 }
78 for (auto&& v : list) {
79 if (auto l = v.lock()) {
80 l->OnResourceEvent(type, ids);
81 }
82 }
83 }
84 }
AddResourceType(CORE_NS::IResourceType::Ptr p)85 bool FileResourceManager::AddResourceType(CORE_NS::IResourceType::Ptr p)
86 {
87 if (p) {
88 std::unique_lock lock { mutex_ };
89 types_[p->GetResourceType()] = p;
90 }
91 return p != nullptr;
92 }
RemoveResourceType(const CORE_NS::ResourceType & type)93 bool FileResourceManager::RemoveResourceType(const CORE_NS::ResourceType& type)
94 {
95 std::unique_lock lock { mutex_ };
96 types_.erase(type);
97 return true;
98 }
GetResourceTypes() const99 BASE_NS::vector<CORE_NS::IResourceType::Ptr> FileResourceManager::GetResourceTypes() const
100 {
101 std::shared_lock lock { mutex_ };
102 BASE_NS::vector<CORE_NS::IResourceType::Ptr> res;
103 for (auto&& v : types_) {
104 res.push_back(v.second);
105 }
106 return res;
107 }
ReadHeader(const std::string & line)108 CORE_NS::ResourceId FileResourceManager::ReadHeader(const std::string& line)
109 {
110 std::istringstream in(line);
111 std::string name;
112 std::string group;
113 std::string path;
114 std::string type;
115 in >> std::quoted(name) >> std::quoted(group) >> std::quoted(path) >> type;
116 if (!in || type.size() != 36U) {
117 return {};
118 }
119
120 auto data = BASE_NS::make_shared<ResourceData>();
121 data->id =
122 CORE_NS::ResourceId { BASE_NS::string(name.data(), name.size()), BASE_NS::string(group.data(), group.size()) };
123 data->path = BASE_NS::string(path.data(), path.size());
124 data->type = BASE_NS::StringToUid(BASE_NS::string_view(type.data(), type.size()));
125
126 std::string opt;
127 if (in >> opt) {
128 data->optionData = BASE_NS::Base64Decode(opt.c_str());
129 }
130 resources_[data->id.group][data->id.name] = data;
131 return data->id;
132 }
ReadHeaders(CORE_NS::IFile & file,BASE_NS::vector<CORE_NS::ResourceId> & result)133 bool FileResourceManager::ReadHeaders(CORE_NS::IFile& file, BASE_NS::vector<CORE_NS::ResourceId>& result)
134 {
135 std::string vec;
136 vec.resize(file.GetLength());
137 file.Read(vec.data(), vec.size());
138 std::istringstream ss(vec);
139 std::string line;
140 while (std::getline(ss, line)) {
141 if (!line.empty() && line[0] != '#') {
142 auto res = ReadHeader(line);
143 if (!res.IsValid()) {
144 return false;
145 }
146 result.push_back(std::move(res));
147 }
148 }
149 return true;
150 }
Import(BASE_NS::string_view url)151 FileResourceManager::Result FileResourceManager::Import(BASE_NS::string_view url)
152 {
153 BASE_NS::vector<CORE_NS::ResourceId> result;
154 {
155 std::unique_lock lock { mutex_ };
156 auto f = fileManager_->OpenFile(url);
157 if (!f) {
158 CORE_LOG_W("Failed to open resource manager file: %s", BASE_NS::string(url).c_str());
159 return Result::FILE_NOT_FOUND;
160 }
161 if (!ReadHeaders(*f, result)) {
162 return Result::INVALID_FILE;
163 }
164 }
165 Notify(result, IResourceListener::EventType::ADDED);
166 return Result::OK;
167 }
GetResourceInfos(const BASE_NS::array_view<const CORE_NS::MatchingResourceId> & selection) const168 BASE_NS::vector<CORE_NS::ResourceInfo> FileResourceManager::GetResourceInfos(
169 const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection) const
170 {
171 std::shared_lock lock { mutex_ };
172 BASE_NS::vector<CORE_NS::ResourceInfo> infos;
173 for (auto&& g : resources_) {
174 for (auto&& v : g.second) {
175 if (ResourceMatches(selection, v.second->id)) {
176 infos.push_back(*v.second);
177 }
178 }
179 }
180 return infos;
181 }
GetResourceGroups() const182 BASE_NS::vector<BASE_NS::string> FileResourceManager::GetResourceGroups() const
183 {
184 std::shared_lock lock { mutex_ };
185 BASE_NS::vector<BASE_NS::string> groups;
186 for (auto&& g : resources_) {
187 groups.push_back(g.first);
188 }
189 return groups;
190 }
GetResourceInfo(const CORE_NS::ResourceId & id) const191 CORE_NS::ResourceInfo FileResourceManager::GetResourceInfo(const CORE_NS::ResourceId& id) const
192 {
193 std::shared_lock lock { mutex_ };
194 auto git = resources_.find(id.group);
195 if (git != resources_.end()) {
196 auto it = git->second.find(id.name);
197 return it != git->second.end() ? CORE_NS::ResourceInfo { *it->second } : CORE_NS::ResourceInfo {};
198 }
199 return {};
200 }
FindResource(const CORE_NS::ResourceId & id)201 BASE_NS::shared_ptr<ResourceData> FileResourceManager::FindResource(const CORE_NS::ResourceId& id)
202 {
203 auto git = resources_.find(id.group);
204 if (git == resources_.end()) {
205 return nullptr;
206 }
207 auto it = git->second.find(id.name);
208 if (it == git->second.end()) {
209 return nullptr;
210 }
211 return it->second;
212 }
GetResource(const CORE_NS::ResourceId & id,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)213 CORE_NS::IResource::Ptr FileResourceManager::GetResource(
214 const CORE_NS::ResourceId& id, const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
215 {
216 {
217 std::shared_lock lock { mutex_ };
218 if (auto res = FindResource(id)) {
219 if (res->object) {
220 return res->object;
221 }
222 }
223 }
224 return ConstructResource(id, context);
225 }
GetResources(const BASE_NS::array_view<const CORE_NS::MatchingResourceId> & selection,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)226 BASE_NS::vector<CORE_NS::IResource::Ptr> FileResourceManager::GetResources(
227 const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection,
228 const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
229 {
230 BASE_NS::vector<CORE_NS::IResource::Ptr> res;
231 BASE_NS::vector<CORE_NS::ResourceId> populate;
232 {
233 std::shared_lock lock { mutex_ };
234 for (auto&& g : resources_) {
235 for (auto&& v : g.second) {
236 auto& r = *v.second;
237 if (ResourceMatches(selection, r.id)) {
238 if (r.object) {
239 res.push_back(r.object);
240 } else {
241 populate.push_back(r.id);
242 }
243 }
244 }
245 }
246 }
247 for (auto&& v : populate) {
248 if (auto p = ConstructResource(v, context)) {
249 res.push_back(p);
250 }
251 }
252 return res;
253 }
SaveResourceHeader(const ResourceData & r,CORE_NS::IFile & file)254 static void SaveResourceHeader(const ResourceData& r, CORE_NS::IFile& file)
255 {
256 std::ostringstream out;
257 out << std::quoted(r.id.name.c_str()) << " " << std::quoted(r.id.group.c_str()) << " "
258 << std::quoted(r.path.c_str()) << " " << BASE_NS::to_string(r.type).c_str() << " ";
259 out << BASE_NS::Base64Encode(r.optionData).c_str();
260 out << "\n";
261 auto buf = out.str();
262 file.Write(buf.data(), buf.size());
263 }
SaveResourceData(const ResourceData & r)264 FileResourceManager::Result FileResourceManager::SaveResourceData(const ResourceData& r)
265 {
266 if (!r.object) {
267 return Result::NO_RESOURCE_DATA;
268 }
269 auto it = types_.find(r.type);
270 if (it == types_.end()) {
271 CORE_LOG_W("Resource type not registered [type=%s]", BASE_NS::to_string(r.type).c_str());
272 return Result::NO_RESOURCE_TYPE;
273 }
274 if (!r.path.empty()) {
275 MemFile data;
276 if (!it->second->SaveResource(r.object, CORE_NS::IResourceType::StorageInfo{nullptr, &data, r.id, r.path,
277 GetSelf<IResourceManager>()})) {
278 CORE_LOG_W("Failed to save resource: %s", r.id.ToString().c_str());
279 return Result::EXPORT_FAILURE;
280 }
281 if (data.GetLength()) {
282 auto f = fileManager_->CreateFile(r.path);
283 if (!f) {
284 CORE_LOG_W("Failed to open resource file: %s", r.path.c_str());
285 return Result::FILE_WRITE_ERROR;
286 }
287 f->Write(data.RawData(), data.GetLength());
288 }
289 }
290 return Result::OK;
291 }
UpdateOptions(ResourceData & r)292 bool FileResourceManager::UpdateOptions(ResourceData& r)
293 {
294 if (r.object) {
295 auto it = types_.find(r.type);
296 if (it == types_.end()) {
297 CORE_LOG_W("Resource type not registered [type=%s]", BASE_NS::to_string(r.type).c_str());
298 return false;
299 }
300 MemFile opts;
301 if (!it->second->SaveResource(r.object, CORE_NS::IResourceType::StorageInfo{&opts, nullptr, r.id, r.path,
302 GetSelf<IResourceManager>()})) {
303 CORE_LOG_W("Failed to save resource options");
304 return false;
305 }
306 r.optionData = opts.Data();
307 }
308 return true;
309 }
Export(BASE_NS::string_view filePath,const BASE_NS::array_view<const CORE_NS::MatchingResourceId> & selection)310 FileResourceManager::Result FileResourceManager::Export(
311 BASE_NS::string_view filePath, const BASE_NS::array_view<const CORE_NS::MatchingResourceId>& selection)
312 {
313 std::unique_lock lock { mutex_ };
314 auto f = fileManager_->CreateFile(filePath);
315 if (!f) {
316 CORE_LOG_W("Failed to create resource manager file: %s", BASE_NS::string(filePath).c_str());
317 return Result::FILE_WRITE_ERROR;
318 }
319 BASE_NS::string vstr = "# Lume Resource Index Version 1\n";
320 f->Write(vstr.data(), vstr.size());
321 for (auto&& g : resources_) {
322 for (auto&& v : g.second) {
323 auto& res = *v.second;
324 if (ResourceMatches(selection, res.id)) {
325 UpdateOptions(res);
326 SaveResourceHeader(res, *f);
327 SaveResourceData(res);
328 }
329 }
330 }
331 return Result::OK;
332 }
333
AddResource(const CORE_NS::IResource::Ptr & resource)334 bool FileResourceManager::AddResource(const CORE_NS::IResource::Ptr& resource)
335 {
336 return AddResource(resource, resource->GetResourceId().name);
337 }
AddResource(const CORE_NS::IResource::Ptr & resource,BASE_NS::string_view path)338 bool FileResourceManager::AddResource(const CORE_NS::IResource::Ptr& resource, BASE_NS::string_view path)
339 {
340 {
341 std::unique_lock lock { mutex_ };
342 auto it = types_.find(resource->GetResourceType());
343 if (it == types_.end()) {
344 CORE_LOG_W(
345 "Resource type not registered [type=%s]", BASE_NS::to_string(resource->GetResourceType()).c_str());
346 return false;
347 }
348 auto id = resource->GetResourceId();
349 ResourceData d;
350 d.object = resource;
351 d.type = resource->GetResourceType();
352 d.id = id;
353 d.path = path;
354 MemFile opts;
355 if (!it->second->SaveResource(resource, CORE_NS::IResourceType::StorageInfo{&opts, nullptr, d.id, d.path,
356 GetSelf<IResourceManager>()})) {
357 CORE_LOG_W("Failed to save resource options");
358 return false;
359 }
360 d.optionData = opts.Data();
361 resources_[id.group][id.name] = BASE_NS::make_shared<ResourceData>(BASE_NS::move(d));
362 }
363 Notify({ resource->GetResourceId() }, IResourceListener::EventType::ADDED);
364 return true;
365 }
AddResource(const CORE_NS::ResourceId & id,const CORE_NS::ResourceType & type,BASE_NS::string_view path,const CORE_NS::IResourceOptions::ConstPtr & options)366 bool FileResourceManager::AddResource(const CORE_NS::ResourceId& id, const CORE_NS::ResourceType& type,
367 BASE_NS::string_view path, const CORE_NS::IResourceOptions::ConstPtr& options)
368 {
369 {
370 std::unique_lock lock { mutex_ };
371 auto it = types_.find(type);
372 if (it == types_.end()) {
373 CORE_LOG_W("Resource type not registered [type=%s]", BASE_NS::to_string(type).c_str());
374 return false;
375 }
376 ResourceData d;
377 d.type = type;
378 d.id = id;
379 d.path = path;
380 if (options) {
381 MemFile opts;
382 options->Save(opts, GetSelf<IResourceManager>());
383 d.optionData = opts.Data();
384 }
385 resources_[id.group][id.name] = BASE_NS::make_shared<ResourceData>(BASE_NS::move(d));
386 }
387 Notify({ id }, IResourceListener::EventType::ADDED);
388 return true;
389 }
ReloadResource(const CORE_NS::IResource::Ptr & resource,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)390 bool FileResourceManager::ReloadResource(
391 const CORE_NS::IResource::Ptr& resource, const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
392 {
393 CORE_NS::IFile::Ptr f;
394 MemFile opts;
395 BASE_NS::string path;
396 CORE_NS::IResourceType::Ptr type;
397 {
398 std::unique_lock lock { mutex_ };
399 auto it = types_.find(resource->GetResourceType());
400 if (it == types_.end()) {
401 CORE_LOG_W(
402 "Resource type not registered [type=%s]", BASE_NS::to_string(resource->GetResourceType()).c_str());
403 return false;
404 }
405 type = it->second;
406 auto res = FindResource(resource->GetResourceId());
407 if (!res) {
408 CORE_LOG_W("No such resource: %s", resource->GetResourceId().ToString().c_str());
409 return false;
410 }
411 path = res->path;
412 if (!path.empty()) {
413 f = fileManager_->OpenFile(path);
414 if (!f) {
415 CORE_LOG_W("Failed to open resource file: %s", path.c_str());
416 return false;
417 }
418 }
419 opts = MemFile(res->optionData);
420 }
421 CORE_NS::IResourceType::StorageInfo info { &opts, f.get(), resource->GetResourceId(), path,
422 GetSelf<IResourceManager>(), context };
423 return type->ReloadResource(info, resource);
424 }
RenameResource(const CORE_NS::ResourceId & id,const CORE_NS::ResourceId & newId)425 bool FileResourceManager::RenameResource(const CORE_NS::ResourceId& id, const CORE_NS::ResourceId& newId)
426 {
427 {
428 std::unique_lock lock { mutex_ };
429 auto git = resources_.find(id.group);
430 if (git == resources_.end()) {
431 return false;
432 }
433 auto it = git->second.find(id.name);
434 if (it == git->second.end()) {
435 return false;
436 }
437 auto& v = resources_[newId.group][newId.name];
438 if (v) {
439 return false;
440 }
441 v = it->second;
442 git->second.erase(it);
443 }
444
445 Notify({ id }, IResourceListener::EventType::REMOVED);
446 Notify({ newId }, IResourceListener::EventType::ADDED);
447 return true;
448 }
PurgeResource(const CORE_NS::ResourceId & id)449 bool FileResourceManager::PurgeResource(const CORE_NS::ResourceId& id)
450 {
451 std::unique_lock lock { mutex_ };
452 if (auto res = FindResource(id)) {
453 res->object = nullptr;
454 return true;
455 }
456 CORE_LOG_W("No such resource: %s", id.ToString().c_str());
457 return false;
458 }
RemoveResource(const CORE_NS::ResourceId & id)459 bool FileResourceManager::RemoveResource(const CORE_NS::ResourceId& id)
460 {
461 bool res = false;
462 {
463 std::unique_lock lock { mutex_ };
464 auto git = resources_.find(id.group);
465 res = git != resources_.end() && git->second.erase(id.name) == 1;
466 }
467 if (res) {
468 Notify({ id }, IResourceListener::EventType::REMOVED);
469 }
470 return res;
471 }
PurgeGroup(BASE_NS::string_view group)472 size_t FileResourceManager::PurgeGroup(BASE_NS::string_view group)
473 {
474 size_t count = 0;
475 std::unique_lock lock { mutex_ };
476 auto git = resources_.find(group);
477 if (git != resources_.end()) {
478 for (auto it = git->second.begin(); it != git->second.end(); ++it) {
479 if (it->second->object) {
480 it->second->object = nullptr;
481 ++count;
482 }
483 }
484 }
485 return count;
486 }
RemoveGroup(BASE_NS::string_view group)487 bool FileResourceManager::RemoveGroup(BASE_NS::string_view group)
488 {
489 bool res = false;
490 BASE_NS::vector<CORE_NS::ResourceId> result;
491 {
492 std::unique_lock lock { mutex_ };
493 auto it = resources_.find(group);
494 res = it != resources_.end();
495 if (res) {
496 for (auto&& g : it->second) {
497 result.push_back(g.second->id);
498 }
499 resources_.erase(it);
500 }
501 }
502 if (res) {
503 Notify(result, IResourceListener::EventType::REMOVED);
504 }
505 return res;
506 }
RemoveAllResources()507 void FileResourceManager::RemoveAllResources()
508 {
509 BASE_NS::vector<CORE_NS::ResourceId> result;
510 {
511 std::unique_lock lock { mutex_ };
512 for (auto&& g : resources_) {
513 for (auto&& v : g.second) {
514 result.push_back(v.second->id);
515 }
516 }
517 resources_.clear();
518 }
519 Notify(result, IResourceListener::EventType::REMOVED);
520 }
521
ExportResourcePayload(const CORE_NS::IResource::Ptr & resource)522 FileResourceManager::Result FileResourceManager::ExportResourcePayload(const CORE_NS::IResource::Ptr& resource)
523 {
524 std::shared_lock lock { mutex_ };
525 auto res = FindResource(resource->GetResourceId());
526 if (!res) {
527 CORE_LOG_W("No such resource: %s", resource->GetResourceId().ToString().c_str());
528 return Result::NO_RESOURCE_DATA;
529 }
530 return SaveResourceData(*res);
531 }
SetFileManager(CORE_NS::IFileManager::Ptr fileManager)532 void FileResourceManager::SetFileManager(CORE_NS::IFileManager::Ptr fileManager)
533 {
534 std::unique_lock lock { mutex_ };
535 fileManager_ = BASE_NS::move(fileManager);
536 }
ConstructResource(const CORE_NS::ResourceId & id,const BASE_NS::shared_ptr<CORE_NS::IInterface> & context)537 CORE_NS::IResource::Ptr FileResourceManager::ConstructResource(
538 const CORE_NS::ResourceId& id, const BASE_NS::shared_ptr<CORE_NS::IInterface>& context)
539 {
540 BASE_NS::shared_ptr<ResourceData> res;
541 CORE_NS::IResourceType::ConstPtr type;
542 BASE_NS::string path;
543 BASE_NS::vector<uint8_t> optionData;
544 CORE_NS::IFileManager::Ptr fileManager;
545
546 {
547 std::shared_lock lock { mutex_ };
548 res = FindResource(id);
549 if (res) {
550 auto tit = types_.find(res->type);
551 if (tit != types_.end()) {
552 type = tit->second;
553 path = res->path;
554 optionData = res->optionData;
555 fileManager = fileManager_;
556 }
557 }
558 }
559
560 CORE_NS::IResource::Ptr resObj;
561 if (res && type) {
562 CORE_NS::IFile::Ptr f;
563 if (!path.empty()) {
564 f = fileManager->OpenFile(path);
565 if (!f) {
566 CORE_LOG_W("Failed to open resource file: %s", path.c_str());
567 return nullptr;
568 }
569 }
570 MemFile opts(optionData);
571 resObj = type->LoadResource(
572 CORE_NS::IResourceType::StorageInfo { &opts, f.get(), id, path, GetSelf<IResourceManager>(), context });
573 if (resObj) {
574 if (auto i = interface_cast<CORE_NS::ISetResourceId>(resObj)) {
575 i->SetResourceId(id);
576 }
577 } else {
578 CORE_LOG_W("Failed to load resource: %s", id.ToString().c_str());
579 }
580 }
581
582 if (resObj) {
583 std::unique_lock lock { mutex_ };
584 if (res->object) {
585 resObj = res->object;
586 } else {
587 res->object = resObj;
588 }
589 }
590 return resObj;
591 }
592
593 META_END_NAMESPACE()
594