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 "flat_container.h"
16
17#include <algorithm>
18
19#include <base/math/mathf.h>
20
21#include <meta/api/internal/iteration.h>
22#include <meta/base/interface_utils.h>
23#include <meta/interface/intf_containable.h>
24
25META_BEGIN_NAMESPACE()
26
27IObject::Ptr FlatContainer::FindAny(const IContainer::FindOptions& options) const
28{
29    return ContainerBase::FindAnyImpl(options, true);
30}
31
32BASE_NS::vector<IObject::Ptr> FlatContainer::FindAll(const IContainer::FindOptions& options) const
33{
34    return ContainerBase::FindAllImpl(options, true);
35}
36
37bool FlatContainer::Add(const IObject::Ptr& object)
38{
39    if (!object) {
40        return false;
41    }
42    const auto direct = !OnAdding()->HasHandlers();
43    SizeType index = 0;
44    {
45        std::unique_lock lock(mutex_);
46        if (!IsCompatible(object)) {
47            return false;
48        }
49        index = children_.size();
50        if (direct) {
51            children_.push_back(object);
52        }
53    }
54    bool success = true;
55    ChildChangedInfo info { object, index, parent_ };
56    if (!direct) {
57        Invoke<IOnChildChanging>(OnAdding(), info, success);
58        if (success) {
59            std::unique_lock lock(mutex_);
60            children_.push_back(object);
61        }
62    }
63    // Calling external interface methods outside of our internal lock
64    if (success) {
65        SetObjectParent(object, interface_pointer_cast<IObject>(parent_));
66        Invoke<IOnChildChanged>(OnAdded(), info);
67    }
68    return success;
69}
70
71bool FlatContainer::Insert(SizeType index, const IObject::Ptr& object)
72{
73    if (!object) {
74        return false;
75    }
76    const auto direct = !OnAdding()->HasHandlers();
77    {
78        std::unique_lock lock(mutex_);
79        if (!IsCompatible(object)) {
80            return false;
81        }
82        index = BASE_NS::Math::min(index, children_.size());
83        if (direct) {
84            children_.insert(children_.begin() + index, object);
85        }
86    }
87    bool success = true;
88    ChildChangedInfo info { object, index, parent_ };
89    if (!direct) {
90        Invoke<IOnChildChanging>(OnAdding(), info, success);
91        if (success) {
92            std::unique_lock lock(mutex_);
93            children_.insert(children_.begin() + index, object);
94        }
95    }
96    // Calling external interface methods outside of our internal lock
97    if (success) {
98        SetObjectParent(object, interface_pointer_cast<IObject>(parent_));
99        Invoke<IOnChildChanged>(OnAdded(), ChildChangedInfo { object, index, parent_ });
100    }
101    return success;
102}
103
104bool FlatContainer::Replace(const IObject::Ptr& child, const IObject::Ptr& replaceWith, bool addAlways)
105{
106    SizeType index = 0;
107    IObject::Ptr added;
108    IObject::Ptr removed;
109    {
110        std::unique_lock lock(mutex_);
111        if (replaceWith && !IsCompatible(replaceWith)) {
112            return false;
113        }
114        for (auto&& v : children_) {
115            if (child == v) {
116                break;
117            }
118            ++index;
119        }
120        if (index < children_.size()) {
121            removed = children_[index];
122            if (removed == replaceWith) {
123                return removed != nullptr;
124            }
125            if (replaceWith) {
126                children_[index] = replaceWith;
127                added = replaceWith;
128            } else {
129                children_.erase(children_.begin() + index);
130            }
131        } else if (addAlways && replaceWith) {
132            children_.push_back(replaceWith);
133            added = replaceWith;
134        }
135    }
136    ChildChangedInfo addedInfo { added, index, parent_ };
137    ChildChangedInfo removedInfo { removed, index, parent_ };
138    bool success = true;
139    if (removed) {
140        Invoke<IOnChildChanging>(OnRemoving(), removedInfo, success);
141        if (!success) {
142            CORE_LOG_E("Failing a remove transaction during replace operation is not supported");
143            success = true;
144        }
145    }
146    if (added) {
147        Invoke<IOnChildChanging>(OnAdding(), addedInfo, success);
148        if (!success) {
149            CORE_LOG_E("Failing an add transaction during replace operation is not supported");
150        }
151    }
152    if (removed) {
153        SetObjectParent(removed, nullptr);
154        Invoke<IOnChildChanged>(OnRemoved(), removedInfo);
155    }
156    if (added) {
157        SetObjectParent(added, interface_pointer_cast<IObject>(parent_));
158        Invoke<IOnChildChanged>(OnAdded(), addedInfo);
159    }
160    return added || removed;
161}
162
163void FlatContainer::SetObjectParent(const IObject::Ptr& object, const IObject::Ptr& parent) const
164{
165    const auto set = interface_cast<IMutableContainable>(object);
166    if (!set) {
167        // Object does not support setting a parent
168        return;
169    }
170    if (const auto cont = interface_cast<IContainable>(object)) {
171        // Remove from old parent (if any)
172        if (const auto old = interface_pointer_cast<IContainer>(cont->GetParent())) {
173            if (old == interface_pointer_cast<IContainer>(parent)) {
174                // The object is already a child of the new parent container
175                return;
176            }
177            old->Remove(object);
178        }
179    }
180    if (!parent) {
181        for (auto&& c : children_) {
182            // we have another, don't remove the parent
183            if (c == object) {
184                return;
185            }
186        }
187    }
188    set->SetParent(parent);
189}
190
191META_END_NAMESPACE()
192