/*
* 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 "composite_object_provider.h"
#include
#include
META_BEGIN_NAMESPACE()
CompositeObjectProvider::CompositeObjectProvider() = default;
CompositeObjectProvider::~CompositeObjectProvider()
{
ForEachShared(GetSelf(), [&](const IObject::Ptr& obj) {
if (auto provider = interface_cast(obj)) {
provider->OnDataAdded()->RemoveHandler(uintptr_t(this));
provider->OnDataRemoved()->RemoveHandler(uintptr_t(this));
provider->OnDataMoved()->RemoveHandler(uintptr_t(this));
}
});
}
bool CompositeObjectProvider::Build(const IMetadata::Ptr&)
{
// todo: the iterates work only for flat container, we don't want to iterate inside children
// set such option for container when implemented,
SetRequiredInterfaces({ IObjectProvider::UID });
OnAdded()->AddHandler(MakeCallback(this, &CompositeObjectProvider::OnProviderAdded));
OnRemoved()->AddHandler(MakeCallback(this, &CompositeObjectProvider::OnProviderRemoved));
OnMoved()->AddHandler(MakeCallback(this, &CompositeObjectProvider::OnProviderMoved));
META_ACCESS_PROPERTY(CacheHint)->OnChanged()->AddHandler(MakeCallback([&] {
ForEachShared(GetSelf(), [&](const IObject::Ptr& obj) {
if (auto provider = interface_cast(obj)) {
provider->CacheHint()->SetValue(CacheHint()->GetValue());
}
});
}));
return true;
}
IObject::Ptr CompositeObjectProvider::CreateObject(const DataModelIndex& index)
{
size_t ni = index.Index();
if (auto p = FindProvider(ni)) {
if (auto o = p->CreateObject(DataModelIndex { ni, index.GetDimensionPointer() })) {
objects_[o.get()] = p;
return o;
}
}
return nullptr;
}
bool CompositeObjectProvider::DisposeObject(const META_NS::IObject::Ptr& item)
{
auto it = objects_.find(item.get());
if (it == objects_.end()) {
return false;
}
auto p = it->second;
objects_.erase(it);
return p->DisposeObject(item);
}
IObjectProvider* CompositeObjectProvider::FindProvider(size_t& index) const
{
size_t curSize = 0;
IObjectProvider* res {};
IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
if (auto provider = interface_cast(obj)) {
auto prevSize = curSize;
curSize += provider->GetObjectCount();
if (index < curSize) {
index = index - prevSize;
res = provider;
return false;
}
}
return true;
});
return res;
}
size_t CompositeObjectProvider::GetObjectCount(const DataModelIndex& index) const
{
// is the size asked for other than first dimension?
if (index.IsValid()) {
size_t ni = index.Index();
if (auto p = FindProvider(ni)) {
return p->GetObjectCount(DataModelIndex { ni, index.GetDimensionPointer() });
}
}
// calculate size for first dimension
size_t res = 0;
IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
if (auto provider = interface_cast(obj)) {
res += provider->GetObjectCount();
}
return true;
});
return res;
}
size_t CompositeObjectProvider::CalculateIndex(const IObjectProvider::Ptr& provider, size_t localIndex) const
{
size_t base = 0;
bool ret = IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
if (auto prov = interface_cast(obj)) {
if (prov == provider.get()) {
return false;
}
base += prov->GetObjectCount();
}
return true;
});
return ret ? base + localIndex : -1;
}
void CompositeObjectProvider::OnAddedProviderData(
const IObjectProvider::Ptr& provider, DataModelIndex base, size_t count)
{
if (provider && base.IsValid()) {
size_t index = CalculateIndex(provider, base.Index());
if (index != -1) {
META_ACCESS_EVENT(OnDataAdded)->Invoke(DataModelIndex { index, base.GetDimensionPointer() }, count);
}
}
}
void CompositeObjectProvider::OnRemovedProviderData(
const IObjectProvider::Ptr& provider, DataModelIndex base, size_t count)
{
if (provider && base.IsValid()) {
size_t index = CalculateIndex(provider, base.Index());
if (index != -1) {
META_ACCESS_EVENT(OnDataRemoved)->Invoke(DataModelIndex { index, base.GetDimensionPointer() }, count);
}
}
}
void CompositeObjectProvider::OnMovedProviderData(
const IObjectProvider::Ptr& provider, DataModelIndex from, size_t count, DataModelIndex to)
{
if (provider && from.IsValid() && to.IsValid()) {
size_t fromIndex = CalculateIndex(provider, from.Index());
size_t toIndex = CalculateIndex(provider, to.Index());
if (fromIndex != -1 && toIndex != -1) {
META_ACCESS_EVENT(OnDataMoved)
->Invoke(DataModelIndex { fromIndex, from.GetDimensionPointer() }, count,
DataModelIndex { toIndex, to.GetDimensionPointer() });
}
}
}
void CompositeObjectProvider::OnProviderAdded(const ChildChangedInfo& info)
{
auto provider = interface_pointer_cast(info.object);
if (!provider) {
return;
}
auto added = provider->OnDataAdded()->AddHandler(
MakeCallback(
[this](auto provider, DataModelIndex base, size_t count) { OnAddedProviderData(provider, base, count); },
provider),
uintptr_t(this));
auto removed = provider->OnDataRemoved()->AddHandler(
MakeCallback(
[this](auto provider, DataModelIndex base, size_t count) { OnRemovedProviderData(provider, base, count); },
provider),
uintptr_t(this));
auto moved = provider->OnDataMoved()->AddHandler(
MakeCallback([this](auto provider, DataModelIndex from, size_t count,
DataModelIndex to) { OnMovedProviderData(provider, from, count, to); },
provider),
uintptr_t(this));
provider->CacheHint()->SetValue(CacheHint()->GetValue());
if (provider->GetObjectCount() > 0) {
size_t index = CalculateIndex(provider, 0);
if (index != -1) {
META_ACCESS_EVENT(OnDataAdded)->Invoke(DataModelIndex { index }, provider->GetObjectCount());
}
}
}
size_t CompositeObjectProvider::CalculateIndexBase(size_t provider) const
{
size_t index = 0;
size_t base = 0;
IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
if (auto prov = interface_cast(obj)) {
if (++index > provider) {
return false;
}
base += prov->GetObjectCount();
}
return true;
});
return base;
}
void CompositeObjectProvider::OnProviderRemoved(const ChildChangedInfo& info)
{
auto provider = interface_pointer_cast(info.object);
if (!provider) {
return;
}
provider->OnDataAdded()->RemoveHandler(uintptr_t(this));
provider->OnDataRemoved()->RemoveHandler(uintptr_t(this));
provider->OnDataMoved()->RemoveHandler(uintptr_t(this));
if (provider->GetObjectCount() > 0) {
size_t index = CalculateIndexBase(info.index);
META_ACCESS_EVENT(OnDataRemoved)->Invoke(DataModelIndex { index }, provider->GetObjectCount());
}
}
void CompositeObjectProvider::OnProviderMoved(const ChildMovedInfo& info)
{
auto provider = interface_pointer_cast(info.object);
if (!provider || provider->GetObjectCount() == 0) {
return;
}
size_t index = 0;
size_t fromAdjust = info.to < info.from;
size_t from = CalculateIndexBase(info.from + fromAdjust);
if (fromAdjust) {
// in case we moved it already before to the from-location, we need to remove it to get the previous state
from -= provider->GetObjectCount();
}
auto to = CalculateIndex(provider, 0);
if (to != -1) {
// if the provider was before where it is currently, we need to add its content to get the previous state
if (info.from < info.to) {
to += provider->GetObjectCount();
}
META_ACCESS_EVENT(OnDataMoved)
->Invoke(DataModelIndex { from }, provider->GetObjectCount(), DataModelIndex { to });
}
}
META_END_NAMESPACE()