* Copyright (c) 2024 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 "object_registry.h"
#include <chrono>
#include <base/containers/fixed_string.h>
#include <base/util/compile_time_hashes.h>
#include <base/util/uid_util.h>
#include <meta/interface/animation/builtin_animations.h>
#include <meta/interface/intf_derived.h>
#include "any.h"
#include "call_context.h"
#include "future.h"
#include "object_data_container.h"
#include "property/bind.h"
#include "property/stack_property.h"
#include "random.h"
#include "ref_uri_util.h"
#define OBJ_REG_LOG(...)
Notes:
* Currently Unregistering object and creating it at the same time can cause the pointer ObjectTypeInfo still
be used after the Unregister function returns.
* Same issue applies to creating and unregistering property.
*/
META_BEGIN_NAMESPACE()
const size_t DISPOSAL_THRESHOLD = 100;
static BASE_NS::Uid GenerateInstanceId(uint64_t random)
{
auto elapsed = std::chrono::high_resolution_clock::now();
auto high = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed.time_since_epoch()).count();
BASE_NS::Uid uid;
uid.data[0] = static_cast<uint64_t>(high);
uid.data[1] = random;
return uid;
}
ObjectRegistry::ObjectRegistry() : random_(CreateXoroshiro128(BASE_NS::FNV1aHash("ToolKitObjectRegistry")))
{}
ObjectRegistry::~ObjectRegistry()
{
queues_.clear();
defaultContext_.reset();
classRegistry_.Clear();
GC();
bool first = true;
auto& pluginRegistry = CORE_NS::GetPluginRegister();
for (;;) {
const auto& types = pluginRegistry.GetTypeInfos(ObjectTypeInfo::UID);
if (types.empty()) {
break;
}
auto object = static_cast<const ObjectTypeInfo* const>(types[0]);
if (first) {
CORE_LOG_F("Object classes not unregistered before object registry death. (force unregister)");
first = false;
}
auto& classInfo = object->GetFactory()->GetClassInfo();
CORE_LOG_F(
"Name: [%s] ClassId [%s]", BASE_NS::string(classInfo.Name()).c_str(), classInfo.Id().ToString().c_str());
pluginRegistry.UnregisterTypeInfo(*types[0]);
}
}
IClassRegistry& ObjectRegistry::GetClassRegistry()
{
return classRegistry_;
}
static void RegisterToPluginRegistry(const ObjectTypeInfo& info)
{
auto& pluginRegistry = CORE_NS::GetPluginRegister();
const auto& types = pluginRegistry.GetTypeInfos(ObjectTypeInfo::UID);
for (auto it = types.begin(); it != types.end(); it++) {
auto object = static_cast<const ObjectTypeInfo* const>(*it);
if (*object == info) {
return;
}
}
pluginRegistry.RegisterTypeInfo(info);
}
static void UnregisterFromPluginRegistry(const BASE_NS::Uid& uid)
{
auto& pluginRegistry = CORE_NS::GetPluginRegister();
const auto& types = pluginRegistry.GetTypeInfos(ObjectTypeInfo::UID);
for (auto it = types.begin(); it != types.end(); it++) {
auto object = static_cast<const ObjectTypeInfo* const>(*it);
if (object->GetFactory()->GetClassInfo().Id() == ObjectId(uid)) {
pluginRegistry.UnregisterTypeInfo(**it);
break;
}
}
}
bool ObjectRegistry::RegisterObjectType(const IClassInfo::Ptr& classInfo)
{
if (!classInfo) {
return false;
}
const auto factory = interface_pointer_cast<IObjectFactory>(classInfo);
if (!factory) {
CORE_LOG_E("ObjectRegistry: The class (%s) being registered does not provide object factory",
classInfo->GetClassInfo().Name().data());
return false;
}
if (!classRegistry_.Register(factory)) {
return false;
}
return true;
}
bool ObjectRegistry::UnregisterObjectType(const IClassInfo::Ptr& classInfo)
{
if (const auto factory = interface_pointer_cast<IObjectFactory>(classInfo)) {
return classRegistry_.Unregister(factory);
}
return false;
}
BASE_NS::string ObjectRegistry::GetClassName(BASE_NS::Uid uid) const
{
return classRegistry_.GetClassName(uid);
}
ObjectRegistry::CreateResult ObjectRegistry::CreateInternal(
const IObjectFactory::ConstPtr fac, BASE_NS::vector<IObject::Ptr>& classes) const
{
ClassInfo info = fac->GetClassInfo();
return CreateResult{ConstructObjectInternal(fac, classes), info.category, info.IsSingleton()};
}
ObjectRegistry::CreateResult ObjectRegistry::CreateInternal(
BASE_NS::Uid uid, BASE_NS::vector<IObject::Ptr>& classes) const
{
if (auto fac = classRegistry_.GetObjectFactory(uid)) {
return CreateInternal(fac, classes);
}
return {false, 0, false};
}
static ObjectId GetBaseClass(const IObjectFactory::ConstPtr& fac)
{
if (auto sdata = fac->GetClassStaticMetadata()) {
if (auto m = GetBaseClassMeta(sdata)) {
return m->classInfo ? m->classInfo->Id() : ObjectId{};
}
}
return {};
}
bool ObjectRegistry::ConstructObjectInternal(
const IObjectFactory::ConstPtr& fac, BASE_NS::vector<IObject::Ptr>& classes) const
{
if (auto obj = fac->CreateInstance()) {
classes.push_back(obj);
auto superUid = GetBaseClass(fac);
if (superUid.IsValid()) {
OBJ_REG_LOG("\tCreate super of %s", GetClassName(superUid).c_str());
auto super = CreateInternal(superUid.ToUid(), classes);
if (!super.successful) {
CORE_LOG_F("Could not create the super class [uid=%s]", superUid.ToString().c_str());
return false;
}
}
return true;
}
return false;
}
void ObjectRegistry::SetObjectInstanceIds(const BASE_NS::vector<IObject::Ptr>& classes, InstanceId instid) const
{
IObject::Ptr obj = classes.front();
IObject::Ptr base;
for (auto it = classes.rbegin(); it != classes.rend(); ++it) {
if (auto o = (*it)->GetInterface<ILifecycle>()) {
o->SetInstanceId(instid);
}
if (auto der = (*it)->GetInterface<IDerived>()) {
if (base) {
OBJ_REG_LOG("\tAssigning instance of %s as super to %s",
BASE_NS::string(base->GetClassName()).c_str(),
BASE_NS::string((*it)->GetClassName()).c_str());
}
der->SetSuperInstance(obj, base);
}
base = *it;
}
}
bool ObjectRegistry::BuildObject(const BASE_NS::vector<IObject::Ptr>& classes, const IMetadata::Ptr& data) const
{
if (classes.empty()) {
return false;
}
IObject::Ptr obj = classes.front();
for (auto it = classes.rbegin(); it != classes.rend(); ++it) {
if (auto ctor = (*it)->GetInterface<ILifecycle>()) {
OBJ_REG_LOG("\tBuilding %s", BASE_NS::string((*it)->GetClassName()).c_str());
if (!ctor->Build(data)) {
return false;
}
}
}
return true;
}
IObject::Ptr ObjectRegistry::Create(ObjectId uid, const CreateInfo& createInfo, const IMetadata::Ptr& data) const
{
CheckGC();
auto fac = classRegistry_.GetObjectFactory(uid.ToUid());
if (!fac) {
CORE_LOG_E("No factory for object [id=%s]", uid.ToString().c_str());
return nullptr;
}
if (fac->ConstructionType() == ClassConstructionType::SIMPLE) {
return fac->CreateInstance();
}
auto instid = createInfo.instanceId;
if (instid == BASE_NS::Uid{}) {
std::unique_lock lock{mutex_};
if (auto so = FindSingleton(uid.ToUid())) {
return so;
}
instid = GenerateInstanceId(random_->GetRandom());
} else {
std::shared_lock lock{mutex_};
if (auto so = FindSingleton(uid.ToUid())) {
return so;
}
auto it = instancesByUid_.find(instid);
if (it != instancesByUid_.end() && !it->second.ptr.expired()) {
CORE_LOG_F("Object with instance id %s already exists.", instid.ToString().c_str());
return {};
}
}
OBJ_REG_LOG("Create instance of %s {instance id %s}", GetClassName(uid).c_str(), instid.ToString().c_str());
BASE_NS::vector<IObject::Ptr> classes;
auto t = CreateInternal(fac, classes);
if (t.successful && !classes.empty()) {
if (PostCreate(uid.ToUid(), instid.ToUid(), t, createInfo, classes, data)) {
return classes.front();
}
}
CORE_LOG_F("Could not create instance of %s", GetClassName(uid.ToUid()).c_str());
return nullptr;
}
bool ObjectRegistry::PostCreate(const BASE_NS::Uid& uid, InstanceId instid, const CreateResult& t,
const CreateInfo& createInfo, const BASE_NS::vector<IObject::Ptr>& classes, const IMetadata::Ptr& data) const
{
SetObjectInstanceIds(classes, instid);
if (!BuildObject(classes, data)) {
CORE_LOG_F("Failed to build object (%s).", GetClassName(uid).c_str());
return false;
}
std::unique_lock lock{mutex_};
auto& i = instancesByUid_[instid];
if (!i.ptr.expired()) {
CORE_LOG_F("Object with instance id %s already exists.", instid.ToString().c_str());
return false;
}
i = ObjectInstance{classes.front(), t.category};
if (t.singleton) {
singletons_[uid] = classes.front();
}
if (createInfo.isGloballyAvailable) {
CORE_LOG_V("Registering global object: %s [%s]", GetClassName(uid).c_str(), instid.ToString().c_str());
globalObjects_[instid] = classes.front();
}
return true;
}
IObject::Ptr ObjectRegistry::Create(ObjectId uid, const CreateInfo& createInfo) const
{
return Create(uid, createInfo, nullptr);
}
IObject::Ptr ObjectRegistry::Create(const META_NS::ClassInfo& info, const CreateInfo& createInfo) const
{
return Create(info.Id(), createInfo);
}
BASE_NS::vector<ObjectCategoryItem> ObjectRegistry::GetAllCategories() const
{
static const BASE_NS::vector<ObjectCategoryItem> items = {{ObjectCategoryBits::WIDGET, "Widgets"},
{ObjectCategoryBits::ANIMATION, "Animations"},
{ObjectCategoryBits::LAYOUT, "Layouts"},
{ObjectCategoryBits::CURVE, "Curves"},
{ObjectCategoryBits::SHAPE, "Shapes"},
{ObjectCategoryBits::CONTAINER, "Containers"},
{ObjectCategoryBits::INTERNAL, "Internals"},
{ObjectCategoryBits::APPLICATION, "Application specifics"},
{ObjectCategoryBits::ANIMATION_MODIFIER, "Animation modifier"},
{ObjectCategoryBits::NO_CATEGORY, "Not categorized"}};
return items;
}
IObjectFactory::ConstPtr ObjectRegistry::GetObjectFactory(const ObjectId& uid) const
{
std::shared_lock lock{mutex_};
return classRegistry_.GetObjectFactory(uid.ToUid());
}
BASE_NS::vector<IClassInfo::ConstPtr> ObjectRegistry::GetAllTypes(
ObjectCategoryBits category, bool strict, bool excludeDeprecated) const
{
std::shared_lock lock{mutex_};
return classRegistry_.GetAllTypes(category, strict, excludeDeprecated);
}
void ObjectRegistry::CheckGC() const
{
if (purgeCounter_ > DISPOSAL_THRESHOLD && disposalInProgress_.test_and_set()) {
{
std::unique_lock lock{disposalMutex_};
disposalsStorage_.swap(disposals_);
purgeCounter_ = 0;
}
DoDisposal(disposalsStorage_);
disposalsStorage_.clear();
disposalInProgress_.clear();
}
}
void ObjectRegistry::GC() const
{
for (auto it = instancesByUid_.begin(); it != instancesByUid_.end();) {
if (it->second.ptr.expired()) {
it = instancesByUid_.erase(it);
} else {
++it;
}
}
for (auto it = singletons_.begin(); it != singletons_.end();) {
if (it->second.expired()) {
it = singletons_.erase(it);
} else {
++it;
}
}
for (auto it = globalObjects_.begin(); it != globalObjects_.end();) {
if (it->second.expired()) {
it = globalObjects_.erase(it);
} else {
++it;
}
}
}
void ObjectRegistry::Purge()
{
std::unique_lock lock{mutex_};
GC();
}
void ObjectRegistry::DoDisposal(const BASE_NS::vector<InstanceId>& uids) const
{
std::unique_lock lock{mutex_};
for (auto&& v : uids) {
auto it = instancesByUid_.find(v);
if (it != instancesByUid_.end()) {
if (it->second.ptr.expired()) {
instancesByUid_.erase(it);
}
auto it = singletons_.find(v);
if (it != singletons_.end()) {
if (it->second.expired()) {
singletons_.erase(it);
}
}
}
}
}
void ObjectRegistry::DisposeObject(const InstanceId& uid) const
{
std::unique_lock lock{disposalMutex_};
disposals_.push_back(uid);
++purgeCounter_;
}
ICallContext::Ptr ObjectRegistry::ConstructDefaultCallContext() const
{
return ICallContext::Ptr{new DefaultCallContext};
}
BASE_NS::vector<IObject::Ptr> ObjectRegistry::GetAllObjectInstances() const
{
CheckGC();
BASE_NS::vector<IObject::Ptr> result;
std::shared_lock lock{mutex_};
result.reserve(instancesByUid_.size());
for (auto& v : instancesByUid_) {
if (auto strong = v.second.ptr.lock()) {
result.emplace_back(strong);
}
}
return result;
}
BASE_NS::vector<IObject::Ptr> ObjectRegistry::GetAllSingletonObjectInstances() const
{
CheckGC();
BASE_NS::vector<IObject::Ptr> result;
std::shared_lock lock{mutex_};
if (!singletons_.empty()) {
result.reserve(singletons_.size());
for (auto& s : singletons_) {
if (auto strong = s.second.lock()) {
result.push_back(strong);
}
}
}
return result;
}
BASE_NS::vector<IObject::Ptr> ObjectRegistry::GetObjectInstancesByCategory(
ObjectCategoryBits category, bool strict) const
{
CheckGC();
BASE_NS::vector<IObject::Ptr> result;
std::shared_lock lock{mutex_};
for (auto& i : instancesByUid_) {
if (CheckCategoryBits(static_cast<ObjectCategoryBits>(i.second.category), category, strict)) {
if (auto strong = i.second.ptr.lock()) {
result.emplace_back(strong);
}
}
}
return result;
}
IObject::Ptr ObjectRegistry::FindSingleton(const BASE_NS::Uid uid) const
{
auto it = singletons_.find(uid);
return it != singletons_.end() ? it->second.lock() : nullptr;
}
IObject::Ptr ObjectRegistry::GetObjectInstanceByInstanceId(InstanceId uid) const
{
if (uid == BASE_NS::Uid()) {
return nullptr;
}
CheckGC();
std::shared_lock lock{mutex_};
auto sing = FindSingleton(uid.ToUid());
if (sing) {
return sing;
}
auto it2 = instancesByUid_.find(uid);
if (it2 != instancesByUid_.end()) {
if (auto strong = it2->second.ptr.lock()) {
return strong;
}
CORE_LOG_D("The instance you are trying to find has already died");
}
return nullptr;
}
BASE_NS::string ObjectRegistry::ExportToString(const IObjectRegistryExporter::Ptr& exporter) const
{
return exporter ? exporter->ExportRegistry(this) : "";
}
IObjectContext::Ptr ObjectRegistry::GetDefaultObjectContext() const
{
{
std::shared_lock lock{mutex_};
if (defaultContext_) {
return defaultContext_;
}
}
IObjectContext::Ptr context = interface_pointer_cast<IObjectContext>(
Create(ClassId::ObjectContext, {GlobalObjectInstance::DEFAULT_OBJECT_CONTEXT, true}));
std::unique_lock lock{mutex_};
if (!defaultContext_) {
defaultContext_ = context;
}
CORE_ASSERT_MSG(defaultContext_, "Failed to create default object context");
return defaultContext_;
}
ITaskQueue::Ptr ObjectRegistry::GetTaskQueue(const BASE_NS::Uid& queueId) const
{
std::shared_lock lock{mutex_};
if (auto queue = queues_.find(queueId); queue != queues_.end()) {
return queue->second;
}
CORE_LOG_W("Cannot get task queue, task queue not registered: %s", BASE_NS::to_string(queueId).data());
return {};
}
bool ObjectRegistry::RegisterTaskQueue(const ITaskQueue::Ptr& queue, const BASE_NS::Uid& queueId)
{
std::unique_lock lock{mutex_};
if (!queue) {
if (auto existing = queues_.find(queueId); existing != queues_.end()) {
queues_.erase(existing);
return true;
}
return false;
}
if (!interface_cast<ITaskQueueThreadInfo>(queue)) {
CORE_LOG_W("The registered task queue does not implement the ITaskQueueThreadInfo."
" This may cause issues when matching threads.");
}
queues_[queueId] = queue;
return true;
}
bool ObjectRegistry::UnregisterTaskQueue(const BASE_NS::Uid& queueId)
{
std::unique_lock lock{mutex_};
if (auto existing = queues_.find(queueId); existing != queues_.end()) {
queues_.erase(existing);
return true;
}
return false;
}
bool ObjectRegistry::HasTaskQueue(const BASE_NS::Uid& queueId) const
{
std::shared_lock lock{mutex_};
return queues_.find(queueId) != queues_.end();
}
bool ObjectRegistry::UnregisterAllTaskQueues()
{
std::unique_lock lock{mutex_};
queues_.clear();
return true;
}
#ifdef WIN32
static ITaskQueue::WeakPtr& GetCurrentTaskQueueImpl()
{
static thread_local ITaskQueue::WeakPtr CurrentTaskQueueStorage;
return CurrentTaskQueueStorage;
}
ITaskQueue::Ptr ObjectRegistry::GetCurrentTaskQueue() const
{
return GetCurrentTaskQueueImpl().lock();
}
ITaskQueue::WeakPtr ObjectRegistry::SetCurrentTaskQueue(ITaskQueue::WeakPtr q)
{
auto& impl = GetCurrentTaskQueueImpl();
auto res = impl;
impl = q;
return res;
}
#else
namespace {
static thread_local ITaskQueue::WeakPtr* CurrentTaskQueueStorage = nullptr;
std::mutex gInstanceLock;
struct link {
ITaskQueue::WeakPtr* data{};
link* next{};
};
link* gInstances = nullptr;
}
void __attribute__((destructor)) ObjectRegistryCalledAtExit()
{
link* cur{};
link* next = gInstances;
gInstances = nullptr;
while (next) {
cur = next;
delete cur->data;
cur->data = nullptr;
next = cur->next;
cur->next = nullptr;
delete cur;
}
}
static void RemoveNode(link* prev, link* cur, link* next)
{
delete cur->data;
if (prev == nullptr) {
gInstances = cur->next;
} else {
prev->next = next;
}
delete cur;
}
static void RemoveTaskQueueTLS(META_NS::ITaskQueue::WeakPtr* remove)
{
std::unique_lock lock{gInstanceLock};
link* prev{};
link* cur{};
link* next = gInstances;
while (next) {
cur = next;
next = cur->next;
if (cur->data == remove) {
RemoveNode(prev, cur, next);
return;
}
prev = cur;
}
}
static ITaskQueue::WeakPtr& GetCurrentTaskQueueImpl()
{
if (!CurrentTaskQueueStorage) {
CurrentTaskQueueStorage = new ITaskQueue::WeakPtr();
std::unique_lock lock{gInstanceLock};
gInstances = new link{CurrentTaskQueueStorage, gInstances};
}
return *CurrentTaskQueueStorage;
}
ITaskQueue::Ptr ObjectRegistry::GetCurrentTaskQueue() const
{
return CurrentTaskQueueStorage ? CurrentTaskQueueStorage->lock() : nullptr;
}
ITaskQueue::WeakPtr ObjectRegistry::SetCurrentTaskQueue(ITaskQueue::WeakPtr q)
{
ITaskQueue::WeakPtr ret = CurrentTaskQueueStorage ? *CurrentTaskQueueStorage : nullptr;
if (q.expired()) {
if (CurrentTaskQueueStorage) {
CurrentTaskQueueStorage->reset();
RemoveTaskQueueTLS(CurrentTaskQueueStorage);
CurrentTaskQueueStorage = nullptr;
}
} else {
if (!CurrentTaskQueueStorage) {
CurrentTaskQueueStorage = new ITaskQueue::WeakPtr();
std::unique_lock lock{gInstanceLock};
gInstances = new link{CurrentTaskQueueStorage, gInstances};
}
*CurrentTaskQueueStorage = q;
}
return ret;
}
#endif
IPromise::Ptr ObjectRegistry::ConstructPromise()
{
return IPromise::Ptr(new Promise);
}
IFuture::Ptr ObjectRegistry::ConstructFutureWithValue(const IAny::Ptr& value)
{
auto f = new Future;
f->SetResult(value);
return IFuture::Ptr(f);
}
void ObjectRegistry::RegisterInterpolator(TypeId propertyTypeUid, BASE_NS::Uid interpolatorClassUid)
{
std::unique_lock lock{mutex_};
interpolatorConstructors_[propertyTypeUid] = interpolatorClassUid;
}
void ObjectRegistry::UnregisterInterpolator(TypeId propertyTypeUid)
{
std::unique_lock lock{mutex_};
interpolatorConstructors_.erase(propertyTypeUid);
}
bool ObjectRegistry::HasInterpolator(TypeId propertyTypeUid) const
{
std::shared_lock lock{mutex_};
return interpolatorConstructors_.contains(propertyTypeUid);
}
IInterpolator::Ptr ObjectRegistry::CreateInterpolator(TypeId propertyTypeUid)
{
TypeId uid;
{
std::shared_lock lock{mutex_};
if (auto it = interpolatorConstructors_.find(propertyTypeUid); it != interpolatorConstructors_.end()) {
uid = it->second;
}
}
if (uid != TypeId{}) {
return interface_pointer_cast<IInterpolator>(Create(uid.ToUid(), CreateInfo{}));
}
CORE_LOG_D("No interpolator for property type %s, falling back to default interpolator",
propertyTypeUid.ToString().c_str());
return interface_pointer_cast<IInterpolator>(IObjectRegistry::Create(ClassId::DefaultInterpolator));
}
const CORE_NS::IInterface* ObjectRegistry::GetInterface(const BASE_NS::Uid& uid) const
{
const CORE_NS::IInterface* result = nullptr;
if (uid == CORE_NS::IInterface::UID) {
const IObjectRegistry* obj = static_cast<const IObjectRegistry*>(this);
result = static_cast<const IInterface*>(obj);
}
if (uid == IObjectRegistry::UID) {
result = static_cast<const IObjectRegistry*>(this);
}
if (uid == ITaskQueueRegistry::UID) {
result = static_cast<const ITaskQueueRegistry*>(this);
}
return result;
}
CORE_NS::IInterface* ObjectRegistry::GetInterface(const BASE_NS::Uid& uid)
{
CORE_NS::IInterface* result = nullptr;
if (uid == CORE_NS::IInterface::UID) {
IObjectRegistry* obj = static_cast<IObjectRegistry*>(this);
result = static_cast<IInterface*>(obj);
}
if (uid == IObjectRegistry::UID) {
result = static_cast<IObjectRegistry*>(this);
}
if (uid == ITaskQueueRegistry::UID) {
result = static_cast<ITaskQueueRegistry*>(this);
}
return result;
}
void ObjectRegistry::Ref()
{}
void ObjectRegistry::Unref()
{}
META_NS::IPropertyRegister& ObjectRegistry::GetPropertyRegister()
{
return *this;
}
bool ObjectRegistry::IsPropertyRegistered(const ObjectId& id) const
{
return id == ClassId::StackProperty;
}
META_NS::IProperty::Ptr ObjectRegistry::Create(const ObjectId& object, BASE_NS::string_view name) const
{
if (object == ClassId::StackProperty) {
auto p = META_NS::IProperty::Ptr(new META_NS::Internal::StackProperty(BASE_NS::string(name)));
if (auto i = interface_cast<IPropertyInternal>(p)) {
i->SetSelf(p);
}
return p;
}
return nullptr;
}
IBind::Ptr ObjectRegistry::CreateBind() const
{
return interface_pointer_cast<IBind>(Create(ClassId::Bind, CreateInfo{}));
}
IAny& ObjectRegistry::InvalidAny() const
{
static DummyAny any;
return any;
}
IAny::Ptr ObjectRegistry::ConstructAny(const ObjectId& id) const
{
std::shared_lock lock{mutex_};
auto it = anyBuilders_.find(id);
return it != anyBuilders_.end() ? it->second->Construct() : nullptr;
}
bool ObjectRegistry::IsAnyRegistered(const ObjectId& id) const
{
std::shared_lock lock{mutex_};
return anyBuilders_.find(id) != anyBuilders_.end();
}
void ObjectRegistry::RegisterAny(BASE_NS::shared_ptr<AnyBuilder> builder)
{
std::unique_lock lock{mutex_};
if (anyBuilders_.find(builder->GetObjectId()) != anyBuilders_.end()) {
CORE_LOG_W("Any already registered [id=%s, type=%s]",
builder->GetObjectId().ToString().c_str(),
builder->GetTypeName().c_str());
}
CORE_LOG_V("Registering Any [%s] for type '%s'",
builder->GetObjectId().ToString().c_str(),
builder->GetTypeName().c_str());
anyBuilders_[builder->GetObjectId()] = builder;
}
void ObjectRegistry::UnregisterAny(const ObjectId& id)
{
std::unique_lock lock{mutex_};
anyBuilders_.erase(id);
}
BASE_NS::vector<ObjectId> ObjectRegistry::GetAllRegisteredAnyTypes() const
{
BASE_NS::vector<ObjectId> all;
std::unique_lock lock{mutex_};
all.reserve(anyBuilders_.size());
for (auto&& b : anyBuilders_) {
all.push_back(b.first);
}
return all;
}
IGlobalSerializationData& ObjectRegistry::GetGlobalSerializationData()
{
return *this;
}
SerializationSettings ObjectRegistry::GetDefaultSettings() const
{
std::shared_lock lock{mutex_};
return defaultSettings_;
}
void ObjectRegistry::SetDefaultSettings(const SerializationSettings& settings)
{
std::unique_lock lock{mutex_};
defaultSettings_ = settings;
}
void ObjectRegistry::RegisterGlobalObject(const IObject::Ptr& object)
{
std::unique_lock lock{mutex_};
if (auto p = interface_cast<IObjectInstance>(object)) {
globalObjects_[p->GetInstanceId()] = object;
}
}
void ObjectRegistry::UnregisterGlobalObject(const IObject::Ptr& object)
{
std::unique_lock lock{mutex_};
if (auto p = interface_cast<IObjectInstance>(object)) {
globalObjects_.erase(p->GetInstanceId());
}
}
IObject::Ptr ObjectRegistry::GetGlobalObject(const InstanceId& id) const
{
std::shared_lock lock{mutex_};
auto it = globalObjects_.find(id);
return it != globalObjects_.end() ? it->second.lock() : nullptr;
}
void ObjectRegistry::RegisterValueSerializer(const IValueSerializer::Ptr& s)
{
std::unique_lock lock{mutex_};
valueSerializers_[s->GetTypeId()] = s;
}
void ObjectRegistry::UnregisterValueSerializer(const TypeId& id)
{
std::unique_lock lock{mutex_};
valueSerializers_.erase(id);
}
IValueSerializer::Ptr ObjectRegistry::GetValueSerializer(const TypeId& id) const
{
std::shared_lock lock{mutex_};
auto it = valueSerializers_.find(id);
return it != valueSerializers_.end() ? it->second : nullptr;
}
IEngineInternalValueAccess::Ptr ObjectRegistry::GetInternalValueAccess(const CORE_NS::PropertyTypeDecl& type) const
{
std::shared_lock lock{mutex_};
auto it = engineInternalAccess_.find(type);
return it != engineInternalAccess_.end() ? it->second : nullptr;
}
void ObjectRegistry::RegisterInternalValueAccess(
const CORE_NS::PropertyTypeDecl& type, IEngineInternalValueAccess::Ptr ptr)
{
std::unique_lock lock{mutex_};
engineInternalAccess_[type] = BASE_NS::move(ptr);
}
void ObjectRegistry::UnregisterInternalValueAccess(const CORE_NS::PropertyTypeDecl& type)
{
std::unique_lock lock{mutex_};
engineInternalAccess_.erase(type);
}
IEngineData& ObjectRegistry::GetEngineData()
{
return *this;
}
BASE_NS::vector<CORE_NS::PropertyTypeDecl> ObjectRegistry::GetAllRegisteredValueAccess() const
{
BASE_NS::vector<CORE_NS::PropertyTypeDecl> all;
{
std::unique_lock lock{mutex_};
all.reserve(engineInternalAccess_.size());
for (auto&& v : engineInternalAccess_) {
all.push_back(v.first);
}
}
return all;
}
IObject::Ptr ObjectRegistry::DefaultResolveObject(const IObjectInstance::Ptr& base, const RefUri& uri) const
{
return META_NS::DefaultResolveObject(base, uri);
}
IMetadata::Ptr ObjectRegistry::ConstructObjectDataContainer()
{
return IMetadata::Ptr(new Internal::ObjectDataContainer);
}
namespace Internal {
constexpr bool IsUniqueName(const BASE_NS::array_view<BASE_NS::string_view>& names, BASE_NS::string_view name)
{
for (auto&& n : names) {
if (n == name) {
return false;
}
}
return true;
}
void MakeNameUnique(const BASE_NS::array_view<BASE_NS::string_view>& names, BASE_NS::string& name, uint32_t index)
{
if (IsUniqueName(names, name)) {
return;
}
constexpr uint32_t POSTFIX_MIN_LENGTH = 3;
const auto length = name.length();
bool remove = length >= POSTFIX_MIN_LENGTH && name.ends_with(')');
for (int64_t pos = static_cast<int64_t>(length) - 1; remove && pos >= 0; pos--) {
if (name[pos] == '(') {
if (!pos || name[pos - 1] == ' ') {
name = name.substr(0, pos);
}
break;
}
}
BASE_NS::string postfix = name.empty() || name.ends_with(" ") ? "" : " ";
postfix.append("(").append(BASE_NS::to_string(++index)).append(")");
name = name + postfix;
MakeNameUnique(names, name, index);
}
}
BASE_NS::string ObjectRegistry::GetUniqueName(
BASE_NS::string_view name, BASE_NS::array_view<BASE_NS::string_view> names) const
{
BASE_NS::string unique(name);
Internal::MakeNameUnique(names, unique, 0);
return unique;
}
IObjectUtil& ObjectRegistry::GetObjectUtil()
{
return *this;
}
META_END_NAMESPACE()