* 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 "container_base.h"
#include <algorithm>
#include <mutex>
#include <base/math/mathf.h>
#include <meta/api/iteration.h>
#include <meta/base/interface_utils.h>
#include <meta/interface/intf_containable.h>
META_BEGIN_NAMESPACE()
void ContainerBase::SetImplementingIContainer(IObject* me, IContainer* c)
{
me_ = me;
impl_ = c;
CORE_ASSERT_MSG(impl_, "ContainerBase with null implementation");
}
void ContainerBase::LockShared() const
{
mutex_.lock_shared();
}
void ContainerBase::UnlockShared() const
{
mutex_.unlock_shared();
}
void ContainerBase::Lock() const
{
mutex_.lock();
}
void ContainerBase::Unlock() const
{
mutex_.unlock();
}
bool ContainerBase::SetProxyParent(const IContainer::Ptr& parent)
{
parent_ = parent;
return true;
}
IObject::Ptr ContainerBase::FindAnyImpl(const IContainer::FindOptions& options, bool isFlat) const
{
IObject::Ptr res;
Internal::ConstIterate(
this,
[&](const IObject::Ptr& obj) {
if (MatchCriteria(options, obj)) {
res = obj;
}
return !res;
},
IterateStrategy{isFlat ? TraversalType::NO_HIERARCHY : options.behavior, LockType::SHARED_LOCK});
return res;
}
BASE_NS::vector<IObject::Ptr> ContainerBase::FindAllImpl(const IContainer::FindOptions& options, bool isFlat) const
{
BASE_NS::vector<IObject::Ptr> res;
Internal::ConstIterate(
this,
[&](const IObject::Ptr& obj) {
if (MatchCriteria(options, obj)) {
res.push_back(obj);
}
return true;
},
IterateStrategy{isFlat ? TraversalType::NO_HIERARCHY : options.behavior, LockType::SHARED_LOCK});
return res;
}
BASE_NS::vector<IObject::Ptr> ContainerBase::GetAll() const
{
std::shared_lock lock(mutex_);
return children_;
}
IObject::Ptr ContainerBase::GetAt(SizeType index) const
{
std::shared_lock lock(mutex_);
if (index >= children_.size()) {
return {};
}
return children_[index];
}
IContainer::SizeType ContainerBase::GetSize() const
{
std::shared_lock lock(mutex_);
return children_.size();
}
IObject::Ptr ContainerBase::FindByName(BASE_NS::string_view name) const
{
std::shared_lock lock(mutex_);
for (const auto& child : children_) {
if (BASE_NS::string_view(child->GetName()) == name) {
return child;
}
}
return {};
}
bool ContainerBase::MatchCriteria(const IContainer::FindOptions& options, const IObject::Ptr& object) const
{
return object && ((!options.strictName && options.name.empty()) || object->GetName() == options.name) &&
CheckInterfaces(object, options.uids, options.strict);
}
bool ContainerBase::Remove(SizeType index)
{
IObject::Ptr child;
{
std::unique_lock lock(mutex_);
if (children_.size() <= index) {
return false;
}
auto it = children_.begin() + index;
child = *it;
children_.erase(it);
}
ChildChangedInfo info{ContainerChangeType::REMOVED, child, parent_, index};
SetObjectParent(child, nullptr);
Invoke<IOnChildChanged>(impl_->EventOnContainerChanged(MetadataQuery::EXISTING), info);
return true;
}
bool ContainerBase::Remove(const IObject::Ptr& child)
{
if (!child) {
return false;
}
SizeType index = 0;
{
std::unique_lock lock(mutex_);
auto it = children_.begin();
for (; it != children_.end() && *it != child; ++it, ++index) {
}
if (it == children_.end()) {
return false;
}
children_.erase(it);
}
ChildChangedInfo info{ContainerChangeType::REMOVED, child, parent_, index};
SetObjectParent(child, nullptr);
Invoke<IOnChildChanged>(impl_->EventOnContainerChanged(MetadataQuery::EXISTING), info);
return true;
}
ChildChangedInfo ContainerBase::MoveInternal(SizeType fromIndex, SizeType toIndex)
{
if (children_.empty()) {
return {};
}
const auto size = children_.size();
fromIndex = BASE_NS::Math::min(fromIndex, size - 1);
toIndex = BASE_NS::Math::min(toIndex, size - 1);
const IObject::Ptr child = children_[fromIndex];
if (fromIndex > toIndex) {
const auto first = children_.rbegin() + (size - fromIndex - 1);
const auto last = children_.rbegin() + (size - toIndex);
std::rotate(first, first + 1, last);
} else if (fromIndex < toIndex) {
const auto first = children_.begin() + fromIndex;
const auto last = children_.begin() + toIndex + 1;
std::rotate(first, first + 1, last);
}
return {ContainerChangeType::MOVED, child, parent_, fromIndex, toIndex};
}
bool ContainerBase::Move(SizeType fromIndex, SizeType toIndex)
{
ChildChangedInfo info;
{
std::unique_lock lock(mutex_);
info = MoveInternal(fromIndex, toIndex);
}
if (info.object) {
if (info.from != info.to) {
Invoke<IOnChildChanged>(impl_->EventOnContainerChanged(MetadataQuery::EXISTING), info);
}
return true;
}
return false;
}
bool ContainerBase::Move(const IObject::Ptr& child, SizeType toIndex)
{
ChildChangedInfo info;
{
std::unique_lock lock(mutex_);
SizeType fromIndex = 0;
for (const auto& c : children_) {
if (c == child) {
info = MoveInternal(fromIndex, toIndex);
break;
}
fromIndex++;
}
}
if (info.object) {
if (info.from != info.to) {
Invoke<IOnChildChanged>(impl_->EventOnContainerChanged(MetadataQuery::EXISTING), info);
}
return true;
}
return false;
}
void ContainerBase::RemoveAll()
{
BASE_NS::vector<IObject::Ptr> children;
{
std::unique_lock lock(mutex_);
children_.swap(children);
}
SizeType index = 0;
for (const auto& child : children) {
ChildChangedInfo info{ContainerChangeType::REMOVED, child, parent_, index++};
SetObjectParent(child, nullptr);
Invoke<IOnChildChanged>(impl_->EventOnContainerChanged(MetadataQuery::EXISTING), info);
}
}
void ContainerBase::InternalRemoveAll()
{
BASE_NS::vector<IObject::Ptr> children;
{
std::unique_lock lock(mutex_);
children_.swap(children);
}
for (const auto& child : children) {
if (auto c = interface_cast<IMutableContainable>(child)) {
c->SetParent(nullptr);
}
}
}
bool ContainerBase::SetRequiredInterfaces(const BASE_NS::vector<TypeId>& interfaces)
{
std::unique_lock lock(mutex_);
required_ = interfaces;
BASE_NS::vector<IObject::Ptr> compatible;
compatible.reserve(children_.size());
for (const auto& child : children_) {
if (IsCompatible(child)) {
compatible.push_back(child);
}
}
children_.swap(compatible);
return true;
}
BASE_NS::vector<TypeId> ContainerBase::GetRequiredInterfaces() const
{
std::shared_lock lock(mutex_);
return required_;
}
bool ContainerBase::IsCompatible(const IObject::Ptr& object) const
{
return ObjectImplementsAll(object, required_);
}
bool ContainerBase::IsAncestorOf(const IObject::ConstPtr& object) const
{
if (!object || !me_) {
return false;
}
if (me_ == object.get()) {
return true;
}
const auto containable = interface_pointer_cast<IContainable>(object);
if (!containable) {
return false;
}
auto parent = containable->GetParent();
while (parent) {
if (parent.get() == me_) {
return true;
}
if (auto parentContainable = interface_cast<IContainable>(parent)) {
parent = parentContainable->GetParent();
} else {
break;
}
}
return false;
}
BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnContainerChanged(MetadataQuery q) const
{
return impl_->EventOnContainerChanged(q);
}
template<typename Cont, typename Func>
static IterationResult IterateImpl(Cont& cont, const Func& func)
{
for (auto&& child : cont) {
auto res = func->Invoke(child);
if (!res.Continue()) {
return res;
}
}
return IterationResult::CONTINUE;
}
IterationResult ContainerBase::Iterate(const IterationParameters& params)
{
auto f = params.function.GetInterface<IIterableCallable<IObject::Ptr>>();
if (!f) {
CORE_LOG_W("Incompatible function with Iterate");
return IterationResult::FAILED;
}
return IterateImpl(children_, f);
}
IterationResult ContainerBase::Iterate(const IterationParameters& params) const
{
auto f = params.function.GetInterface<IIterableConstCallable<IObject::Ptr>>();
if (!f) {
CORE_LOG_W("Incompatible function with Iterate");
return IterationResult::FAILED;
}
return IterateImpl(children_, f);
}
META_END_NAMESPACE()