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 #include "composite_object_provider.h"
16 
17 #include <meta/api/iteration.h>
18 #include <meta/api/make_callback.h>
19 
20 META_BEGIN_NAMESPACE()
21 
22 CompositeObjectProvider::CompositeObjectProvider() = default;
23 
~CompositeObjectProvider()24 CompositeObjectProvider::~CompositeObjectProvider()
25 {
26     ForEachShared(GetSelf(), [&](const IObject::Ptr& obj) {
27         if (auto provider = interface_cast<IObjectProvider>(obj)) {
28             provider->OnDataAdded()->RemoveHandler(uintptr_t(this));
29             provider->OnDataRemoved()->RemoveHandler(uintptr_t(this));
30             provider->OnDataMoved()->RemoveHandler(uintptr_t(this));
31         }
32     });
33 }
34 
Build(const IMetadata::Ptr &)35 bool CompositeObjectProvider::Build(const IMetadata::Ptr&)
36 {
37     // todo: the iterates work only for flat container, we don't want to iterate inside children
38     //  set such option for container when implemented,
39     SetRequiredInterfaces({ IObjectProvider::UID });
40     OnAdded()->AddHandler(MakeCallback<IOnChildChanged>(this, &CompositeObjectProvider::OnProviderAdded));
41     OnRemoved()->AddHandler(MakeCallback<IOnChildChanged>(this, &CompositeObjectProvider::OnProviderRemoved));
42     OnMoved()->AddHandler(MakeCallback<IOnChildMoved>(this, &CompositeObjectProvider::OnProviderMoved));
43 
44     META_ACCESS_PROPERTY(CacheHint)->OnChanged()->AddHandler(MakeCallback<IOnChanged>([&] {
45         ForEachShared(GetSelf(), [&](const IObject::Ptr& obj) {
46             if (auto provider = interface_cast<IObjectProvider>(obj)) {
47                 provider->CacheHint()->SetValue(CacheHint()->GetValue());
48             }
49         });
50     }));
51     return true;
52 }
53 
CreateObject(const DataModelIndex & index)54 IObject::Ptr CompositeObjectProvider::CreateObject(const DataModelIndex& index)
55 {
56     size_t ni = index.Index();
57     if (auto p = FindProvider(ni)) {
58         if (auto o = p->CreateObject(DataModelIndex { ni, index.GetDimensionPointer() })) {
59             objects_[o.get()] = p;
60             return o;
61         }
62     }
63     return nullptr;
64 }
65 
DisposeObject(const META_NS::IObject::Ptr & item)66 bool CompositeObjectProvider::DisposeObject(const META_NS::IObject::Ptr& item)
67 {
68     auto it = objects_.find(item.get());
69     if (it == objects_.end()) {
70         return false;
71     }
72 
73     auto p = it->second;
74     objects_.erase(it);
75     return p->DisposeObject(item);
76 }
77 
FindProvider(size_t & index) const78 IObjectProvider* CompositeObjectProvider::FindProvider(size_t& index) const
79 {
80     size_t curSize = 0;
81     IObjectProvider* res {};
82 
83     IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
84         if (auto provider = interface_cast<IObjectProvider>(obj)) {
85             auto prevSize = curSize;
86             curSize += provider->GetObjectCount();
87             if (index < curSize) {
88                 index = index - prevSize;
89                 res = provider;
90                 return false;
91             }
92         }
93         return true;
94     });
95 
96     return res;
97 }
98 
GetObjectCount(const DataModelIndex & index) const99 size_t CompositeObjectProvider::GetObjectCount(const DataModelIndex& index) const
100 {
101     // is the size asked for other than first dimension?
102     if (index.IsValid()) {
103         size_t ni = index.Index();
104         if (auto p = FindProvider(ni)) {
105             return p->GetObjectCount(DataModelIndex { ni, index.GetDimensionPointer() });
106         }
107     }
108 
109     // calculate size for first dimension
110     size_t res = 0;
111     IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
112         if (auto provider = interface_cast<IObjectProvider>(obj)) {
113             res += provider->GetObjectCount();
114         }
115         return true;
116     });
117     return res;
118 }
119 
CalculateIndex(const IObjectProvider::Ptr & provider,size_t localIndex) const120 size_t CompositeObjectProvider::CalculateIndex(const IObjectProvider::Ptr& provider, size_t localIndex) const
121 {
122     size_t base = 0;
123     bool ret = IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
124         if (auto prov = interface_cast<IObjectProvider>(obj)) {
125             if (prov == provider.get()) {
126                 return false;
127             }
128             base += prov->GetObjectCount();
129         }
130         return true;
131     });
132     return ret ? base + localIndex : -1;
133 }
134 
OnAddedProviderData(const IObjectProvider::Ptr & provider,DataModelIndex base,size_t count)135 void CompositeObjectProvider::OnAddedProviderData(
136     const IObjectProvider::Ptr& provider, DataModelIndex base, size_t count)
137 {
138     if (provider && base.IsValid()) {
139         size_t index = CalculateIndex(provider, base.Index());
140         if (index != -1) {
141             META_ACCESS_EVENT(OnDataAdded)->Invoke(DataModelIndex { index, base.GetDimensionPointer() }, count);
142         }
143     }
144 }
145 
OnRemovedProviderData(const IObjectProvider::Ptr & provider,DataModelIndex base,size_t count)146 void CompositeObjectProvider::OnRemovedProviderData(
147     const IObjectProvider::Ptr& provider, DataModelIndex base, size_t count)
148 {
149     if (provider && base.IsValid()) {
150         size_t index = CalculateIndex(provider, base.Index());
151         if (index != -1) {
152             META_ACCESS_EVENT(OnDataRemoved)->Invoke(DataModelIndex { index, base.GetDimensionPointer() }, count);
153         }
154     }
155 }
156 
OnMovedProviderData(const IObjectProvider::Ptr & provider,DataModelIndex from,size_t count,DataModelIndex to)157 void CompositeObjectProvider::OnMovedProviderData(
158     const IObjectProvider::Ptr& provider, DataModelIndex from, size_t count, DataModelIndex to)
159 {
160     if (provider && from.IsValid() && to.IsValid()) {
161         size_t fromIndex = CalculateIndex(provider, from.Index());
162         size_t toIndex = CalculateIndex(provider, to.Index());
163         if (fromIndex != -1 && toIndex != -1) {
164             META_ACCESS_EVENT(OnDataMoved)
165                 ->Invoke(DataModelIndex { fromIndex, from.GetDimensionPointer() }, count,
166                 DataModelIndex { toIndex, to.GetDimensionPointer() });
167         }
168     }
169 }
170 
OnProviderAdded(const ChildChangedInfo & info)171 void CompositeObjectProvider::OnProviderAdded(const ChildChangedInfo& info)
172 {
173     auto provider = interface_pointer_cast<IObjectProvider>(info.object);
174     if (!provider) {
175         return;
176     }
177 
178     auto added = provider->OnDataAdded()->AddHandler(
179         MakeCallback<IOnDataAdded>(
180             [this](auto provider, DataModelIndex base, size_t count) { OnAddedProviderData(provider, base, count); },
181             provider),
182         uintptr_t(this));
183     auto removed = provider->OnDataRemoved()->AddHandler(
184         MakeCallback<IOnDataRemoved>(
185             [this](auto provider, DataModelIndex base, size_t count) { OnRemovedProviderData(provider, base, count); },
186             provider),
187         uintptr_t(this));
188     auto moved = provider->OnDataMoved()->AddHandler(
189         MakeCallback<IOnDataMoved>([this](auto provider, DataModelIndex from, size_t count,
190                                        DataModelIndex to) { OnMovedProviderData(provider, from, count, to); },
191             provider),
192         uintptr_t(this));
193 
194     provider->CacheHint()->SetValue(CacheHint()->GetValue());
195 
196     if (provider->GetObjectCount() > 0) {
197         size_t index = CalculateIndex(provider, 0);
198         if (index != -1) {
199             META_ACCESS_EVENT(OnDataAdded)->Invoke(DataModelIndex { index }, provider->GetObjectCount());
200         }
201     }
202 }
203 
CalculateIndexBase(size_t provider) const204 size_t CompositeObjectProvider::CalculateIndexBase(size_t provider) const
205 {
206     size_t index = 0;
207     size_t base = 0;
208     IterateShared(GetSelf(), [&](const IObject::Ptr& obj) {
209         if (auto prov = interface_cast<IObjectProvider>(obj)) {
210             if (++index > provider) {
211                 return false;
212             }
213             base += prov->GetObjectCount();
214         }
215         return true;
216     });
217     return base;
218 }
219 
OnProviderRemoved(const ChildChangedInfo & info)220 void CompositeObjectProvider::OnProviderRemoved(const ChildChangedInfo& info)
221 {
222     auto provider = interface_pointer_cast<IObjectProvider>(info.object);
223     if (!provider) {
224         return;
225     }
226 
227     provider->OnDataAdded()->RemoveHandler(uintptr_t(this));
228     provider->OnDataRemoved()->RemoveHandler(uintptr_t(this));
229     provider->OnDataMoved()->RemoveHandler(uintptr_t(this));
230 
231     if (provider->GetObjectCount() > 0) {
232         size_t index = CalculateIndexBase(info.index);
233         META_ACCESS_EVENT(OnDataRemoved)->Invoke(DataModelIndex { index }, provider->GetObjectCount());
234     }
235 }
236 
OnProviderMoved(const ChildMovedInfo & info)237 void CompositeObjectProvider::OnProviderMoved(const ChildMovedInfo& info)
238 {
239     auto provider = interface_pointer_cast<IObjectProvider>(info.object);
240     if (!provider || provider->GetObjectCount() == 0) {
241         return;
242     }
243 
244     size_t index = 0;
245     size_t fromAdjust = info.to < info.from;
246     size_t from = CalculateIndexBase(info.from + fromAdjust);
247     if (fromAdjust) {
248         // in case we moved it already before to the from-location, we need to remove it to get the previous state
249         from -= provider->GetObjectCount();
250     }
251 
252     auto to = CalculateIndex(provider, 0);
253     if (to != -1) {
254         // if the provider was before where it is currently, we need to add its content to get the previous state
255         if (info.from < info.to) {
256             to += provider->GetObjectCount();
257         }
258         META_ACCESS_EVENT(OnDataMoved)
259             ->Invoke(DataModelIndex { from }, provider->GetObjectCount(), DataModelIndex { to });
260     }
261 }
262 
263 META_END_NAMESPACE()