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 "container_base.h"
16 
17 #include <algorithm>
18 
19 #include <base/math/mathf.h>
20 
21 #include <meta/api/iteration.h>
22 #include <meta/base/interface_utils.h>
23 #include <meta/interface/intf_containable.h>
24 
META_BEGIN_NAMESPACE()25 META_BEGIN_NAMESPACE()
26 
27 void ContainerBase::SetImplementingIContainer(IObject* me, IContainer* c)
28 {
29     me_ = me;
30     impl_ = c;
31     CORE_ASSERT(impl_);
32     implPreTrans_ = interface_cast<IContainerPreTransaction>(impl_);
33     CORE_ASSERT(implPreTrans_);
34 }
35 
LockShared() const36 void ContainerBase::LockShared() const
37 {
38     mutex_.lock_shared();
39 }
40 
UnlockShared() const41 void ContainerBase::UnlockShared() const
42 {
43     mutex_.unlock_shared();
44 }
45 
Lock() const46 void ContainerBase::Lock() const
47 {
48     mutex_.lock();
49 }
50 
Unlock() const51 void ContainerBase::Unlock() const
52 {
53     mutex_.unlock();
54 }
55 
SetProxyParent(const IContainer::Ptr & parent)56 bool ContainerBase::SetProxyParent(const IContainer::Ptr& parent)
57 {
58     parent_ = parent;
59     return true;
60 }
61 
FindAnyImpl(const IContainer::FindOptions & options,bool isFlat) const62 IObject::Ptr ContainerBase::FindAnyImpl(const IContainer::FindOptions& options, bool isFlat) const
63 {
64     IObject::Ptr res;
65     ConstIterate(
66         GetSelf(impl_),
67         [&](const IObject::Ptr& obj) {
68             if (MatchCriteria(options, obj)) {
69                 res = obj;
70             }
71             return !res;
72         },
73         IterateStrategy { isFlat ? TraversalType::NO_HIERARCHY : options.behavior, LockType::SHARED_LOCK });
74     return res;
75 }
76 
FindAllImpl(const IContainer::FindOptions & options,bool isFlat) const77 BASE_NS::vector<IObject::Ptr> ContainerBase::FindAllImpl(const IContainer::FindOptions& options, bool isFlat) const
78 {
79     BASE_NS::vector<IObject::Ptr> res;
80     ConstIterate(
81         GetSelf(impl_),
82         [&](const IObject::Ptr& obj) {
83             if (options.name.empty() || obj->GetName() == options.name) {
84                 if (CheckInterfaces(obj, options.uids, options.strict)) {
85                     res.push_back(obj);
86                 }
87             }
88             return true;
89         },
90         IterateStrategy { isFlat ? TraversalType::NO_HIERARCHY : options.behavior, LockType::SHARED_LOCK });
91     return res;
92 }
93 
GetAll() const94 BASE_NS::vector<IObject::Ptr> ContainerBase::GetAll() const
95 {
96     std::shared_lock lock(mutex_);
97     return children_;
98 }
99 
GetAt(SizeType index) const100 IObject::Ptr ContainerBase::GetAt(SizeType index) const
101 {
102     std::shared_lock lock(mutex_);
103     if (index >= children_.size()) {
104         return {};
105     }
106     return children_[index];
107 }
108 
GetSize() const109 IContainer::SizeType ContainerBase::GetSize() const
110 {
111     std::shared_lock lock(mutex_);
112     return children_.size();
113 }
114 
FindByName(BASE_NS::string_view name) const115 IObject::Ptr ContainerBase::FindByName(BASE_NS::string_view name) const
116 {
117     std::shared_lock lock(mutex_);
118     for (const auto& child : children_) {
119         if (child->GetName() == name) {
120             return child;
121         }
122     }
123     return {};
124 }
125 
MatchCriteria(const IContainer::FindOptions & options,const IObject::Ptr & object) const126 bool ContainerBase::MatchCriteria(const IContainer::FindOptions& options, const IObject::Ptr& object) const
127 {
128     return object && (options.name.empty() || object->GetName() == options.name) &&
129            CheckInterfaces(object, options.uids, options.strict);
130 }
131 
Remove(SizeType index)132 bool ContainerBase::Remove(SizeType index)
133 {
134     IObject::Ptr child;
135     const auto direct = !implPreTrans_->OnRemoving()->HasHandlers();
136     {
137         std::unique_lock lock(mutex_);
138         if (children_.size() <= index) {
139             return false;
140         }
141         auto it = children_.begin() + index;
142         child = *it;
143         if (direct) {
144             children_.erase(it);
145         }
146     }
147     bool success = true;
148     ChildChangedInfo info { child, index, parent_ };
149     if (!direct) {
150         Invoke<IOnChildChanging>(implPreTrans_->OnRemoving(), info, success);
151         if (success) {
152             std::unique_lock lock(mutex_);
153             children_.erase(children_.begin() + index);
154         }
155     }
156     if (success) {
157         SetObjectParent(child, nullptr);
158         Invoke<IOnChildChanged>(impl_->OnRemoved(), info);
159     }
160     return success;
161 }
162 
Remove(const IObject::Ptr & child)163 bool ContainerBase::Remove(const IObject::Ptr& child)
164 {
165     if (!child) {
166         return false;
167     }
168     const auto direct = !implPreTrans_->OnRemoving()->HasHandlers();
169     bool success = false;
170     SizeType index = 0;
171     {
172         std::unique_lock lock(mutex_);
173         for (auto it = children_.cbegin(); it != children_.cend(); ++it) {
174             if (*it == child) {
175                 success = true;
176                 if (direct) {
177                     children_.erase(children_.begin() + index);
178                 }
179                 break;
180             }
181             index++;
182         }
183     }
184     if (!success) {
185         return false;
186     }
187     ChildChangedInfo info { child, index, parent_ };
188     if (!direct) {
189         Invoke<IOnChildChanging>(implPreTrans_->OnRemoving(), info, success);
190         if (success) {
191             std::unique_lock lock(mutex_);
192             children_.erase(children_.begin() + index);
193         }
194     }
195     if (success) {
196         SetObjectParent(child, nullptr);
197         Invoke<IOnChildChanged>(impl_->OnRemoved(), info);
198     }
199     return success;
200 }
201 
MoveInternal(SizeType fromIndex,SizeType toIndex)202 ChildMovedInfo ContainerBase::MoveInternal(SizeType fromIndex, SizeType toIndex)
203 {
204     if (children_.empty()) {
205         return {};
206     }
207     const auto size = children_.size();
208     fromIndex = BASE_NS::Math::min(fromIndex, size - 1);
209     toIndex = BASE_NS::Math::min(toIndex, size - 1);
210     const IObject::Ptr child = children_[fromIndex];
211     if (fromIndex == toIndex) {
212         return { child, 0, 0 };
213     }
214     if (fromIndex > toIndex) {
215         const auto first = children_.rbegin() + (size - fromIndex - 1);
216         const auto last = children_.rbegin() + (size - toIndex);
217         std::rotate(first, first + 1, last);
218     } else {
219         const auto first = children_.begin() + fromIndex;
220         const auto last = children_.begin() + toIndex + 1;
221         std::rotate(first, first + 1, last);
222     }
223     return { child, fromIndex, toIndex, parent_ };
224 }
225 
Move(SizeType fromIndex,SizeType toIndex)226 bool ContainerBase::Move(SizeType fromIndex, SizeType toIndex)
227 {
228     ChildMovedInfo info;
229     {
230         std::unique_lock lock(mutex_);
231         info = MoveInternal(fromIndex, toIndex);
232     }
233     if (info.object) {
234         if (info.from != info.to) {
235             Invoke<IOnChildMoved>(impl_->OnMoved(), BASE_NS::move(info));
236         }
237         return true;
238     }
239     return false;
240 }
241 
Move(const IObject::Ptr & child,SizeType toIndex)242 bool ContainerBase::Move(const IObject::Ptr& child, SizeType toIndex)
243 {
244     ChildMovedInfo info;
245     {
246         std::unique_lock lock(mutex_);
247         SizeType fromIndex = 0;
248         for (const auto& c : children_) {
249             if (c == child) {
250                 info = MoveInternal(fromIndex, toIndex);
251                 break;
252             }
253             fromIndex++;
254         }
255     }
256     if (info.object) {
257         if (info.from != info.to) {
258             Invoke<IOnChildMoved>(impl_->OnMoved(), BASE_NS::move(info));
259         }
260         return true;
261     }
262     return false;
263 }
264 
RemoveAll()265 void ContainerBase::RemoveAll()
266 {
267     BASE_NS::vector<IObject::Ptr> children;
268     {
269         std::unique_lock lock(mutex_);
270         children_.swap(children);
271     }
272 
273     SizeType index = 0;
274     for (const auto& child : children) {
275         ChildChangedInfo info { child, index++, parent_ };
276         bool success = true;
277         Invoke<IOnChildChanging>(implPreTrans_->OnRemoving(), info, success); // Ignore result
278         if (!success) {
279             CORE_LOG_E("Failing a remove transaction during remove all operation is not supported");
280         }
281         SetObjectParent(child, nullptr);
282         Invoke<IOnChildChanged>(impl_->OnRemoved(), info);
283     }
284 }
285 
InternalRemoveAll()286 void ContainerBase::InternalRemoveAll()
287 {
288     BASE_NS::vector<IObject::Ptr> children;
289     {
290         std::unique_lock lock(mutex_);
291         children_.swap(children);
292     }
293     for (const auto& child : children) {
294         if (auto c = interface_cast<IMutableContainable>(child)) {
295             c->SetParent(nullptr);
296         }
297     }
298 }
299 
SetRequiredInterfaces(const BASE_NS::vector<TypeId> & interfaces)300 bool ContainerBase::SetRequiredInterfaces(const BASE_NS::vector<TypeId>& interfaces)
301 {
302     std::unique_lock lock(mutex_);
303     required_ = interfaces;
304 
305     BASE_NS::vector<IObject::Ptr> compatible;
306     compatible.reserve(children_.size());
307     for (const auto& child : children_) {
308         if (IsCompatible(child)) {
309             compatible.push_back(child);
310         }
311     }
312     children_.swap(compatible);
313     return true;
314 }
315 
GetRequiredInterfaces() const316 BASE_NS::vector<TypeId> ContainerBase::GetRequiredInterfaces() const
317 {
318     std::shared_lock lock(mutex_);
319     return required_;
320 }
321 
IsCompatible(const IObject::Ptr & object) const322 bool ContainerBase::IsCompatible(const IObject::Ptr& object) const
323 {
324     return ObjectImplementsAll(object, required_);
325 }
326 
IsAncestorOf(const IObject::ConstPtr & object) const327 bool ContainerBase::IsAncestorOf(const IObject::ConstPtr& object) const
328 {
329     if (!object || !me_) {
330         return false;
331     }
332     if (me_ == object.get()) {
333         return true;
334     }
335     const auto containable = interface_pointer_cast<IContainable>(object);
336     if (!containable) {
337         return false;
338     }
339     auto parent = containable->GetParent();
340     while (parent) {
341         if (parent.get() == me_) {
342             return true;
343         }
344         if (auto parentContainable = interface_cast<IContainable>(parent)) {
345             parent = parentContainable->GetParent();
346         } else {
347             break;
348         }
349     }
350     return false;
351 }
352 
EventOnAdded() const353 BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnAdded() const
354 {
355     return impl_->OnAdded();
356 }
357 
EventOnRemoved() const358 BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnRemoved() const
359 {
360     return impl_->OnRemoved();
361 }
362 
EventOnMoved() const363 BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnMoved() const
364 {
365     return impl_->OnMoved();
366 }
367 
EventOnAdding() const368 BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnAdding() const
369 {
370     return implPreTrans_->OnAdding();
371 }
372 
EventOnRemoving() const373 BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnRemoving() const
374 {
375     return implPreTrans_->OnRemoving();
376 }
377 
378 template<typename Cont, typename Func>
IterateImpl(Cont & cont,const Func & func)379 static IterationResult IterateImpl(Cont& cont, const Func& func)
380 {
381     for (auto&& child : cont) {
382         auto res = func->Invoke(child);
383         if (!res.Continue()) {
384             return res;
385         }
386     }
387     return IterationResult::CONTINUE;
388 }
389 
Iterate(const IterationParameters & params)390 IterationResult ContainerBase::Iterate(const IterationParameters& params)
391 {
392     auto f = params.function.GetInterface<IIterableCallable<IObject::Ptr>>();
393     if (!f) {
394         CORE_LOG_W("Incompatible function with Iterate");
395         return IterationResult::FAILED;
396     }
397     return IterateImpl(children_, f);
398 }
399 
Iterate(const IterationParameters & params) const400 IterationResult ContainerBase::Iterate(const IterationParameters& params) const
401 {
402     auto f = params.function.GetInterface<IIterableConstCallable<IObject::Ptr>>();
403     if (!f) {
404         CORE_LOG_W("Incompatible function with Iterate");
405         return IterationResult::FAILED;
406     }
407     return IterateImpl(children_, f);
408 }
409 
410 META_END_NAMESPACE()
411