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
25META_BEGIN_NAMESPACE()
26
27void 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
36void ContainerBase::LockShared() const
37{
38    mutex_.lock_shared();
39}
40
41void ContainerBase::UnlockShared() const
42{
43    mutex_.unlock_shared();
44}
45
46void ContainerBase::Lock() const
47{
48    mutex_.lock();
49}
50
51void ContainerBase::Unlock() const
52{
53    mutex_.unlock();
54}
55
56bool ContainerBase::SetProxyParent(const IContainer::Ptr& parent)
57{
58    parent_ = parent;
59    return true;
60}
61
62IObject::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
77BASE_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
94BASE_NS::vector<IObject::Ptr> ContainerBase::GetAll() const
95{
96    std::shared_lock lock(mutex_);
97    return children_;
98}
99
100IObject::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
109IContainer::SizeType ContainerBase::GetSize() const
110{
111    std::shared_lock lock(mutex_);
112    return children_.size();
113}
114
115IObject::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
126bool 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
132bool 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
163bool 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
202ChildMovedInfo 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
226bool 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
242bool 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
265void 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
286void 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
300bool 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
316BASE_NS::vector<TypeId> ContainerBase::GetRequiredInterfaces() const
317{
318    std::shared_lock lock(mutex_);
319    return required_;
320}
321
322bool ContainerBase::IsCompatible(const IObject::Ptr& object) const
323{
324    return ObjectImplementsAll(object, required_);
325}
326
327bool 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
353BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnAdded() const
354{
355    return impl_->OnAdded();
356}
357
358BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnRemoved() const
359{
360    return impl_->OnRemoved();
361}
362
363BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnMoved() const
364{
365    return impl_->OnMoved();
366}
367
368BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnAdding() const
369{
370    return implPreTrans_->OnAdding();
371}
372
373BASE_NS::shared_ptr<IEvent> ContainerBase::EventOnRemoving() const
374{
375    return implPreTrans_->OnRemoving();
376}
377
378template<typename Cont, typename Func>
379static 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
390IterationResult 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
400IterationResult 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
410META_END_NAMESPACE()
411