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
16 #include "container.h"
17
18 #include <algorithm>
19
20 #include <base/math/mathf.h>
21
22 #include <meta/base/interface_utils.h>
23 #include <meta/interface/intf_containable.h>
24 #include <meta/interface/intf_content.h>
25 #include <meta/interface/property/intf_property.h>
26
META_BEGIN_NAMESPACE()27 META_BEGIN_NAMESPACE()
28
29 IObject::Ptr Container::FindAny(const META_NS::IContainer::FindOptions& options) const
30 {
31 return ContainerBase::FindAnyImpl(options, false);
32 }
33
FindAll(const META_NS::IContainer::FindOptions & options) const34 BASE_NS::vector<IObject::Ptr> Container::FindAll(const META_NS::IContainer::FindOptions& options) const
35 {
36 return ContainerBase::FindAllImpl(options, false);
37 }
38
Add(const META_NS::IObject::Ptr & object)39 bool Container::Add(const META_NS::IObject::Ptr& object)
40 {
41 if (!object) {
42 return false;
43 }
44 const auto direct = !OnAdding()->HasHandlers();
45 SizeType index;
46 {
47 std::unique_lock lock(mutex_);
48 if (!IsCompatible(object) || !CheckLoop(object)) {
49 return false;
50 }
51 for (const auto& child : children_) {
52 if (child == object) {
53 return true; // Already a child of ours
54 }
55 }
56 index = children_.size();
57 if (direct) {
58 children_.push_back(object);
59 }
60 }
61 // Calling external interface methods outside of our internal lock
62 bool success = true;
63 ChildChangedInfo info { object, index, parent_ };
64 if (!direct) {
65 Invoke<IOnChildChanging>(OnAdding(), info, success);
66 if (success) {
67 std::unique_lock lock(mutex_);
68 children_.push_back(object);
69 }
70 }
71 if (success) {
72 SetObjectParent(object, interface_pointer_cast<IObject>(parent_));
73 Invoke<IOnChildChanged>(OnAdded(), info);
74 }
75 return success;
76 }
77
Insert(SizeType index,const IObject::Ptr & object)78 bool Container::Insert(SizeType index, const IObject::Ptr& object)
79 {
80 if (!object) {
81 return false;
82 }
83 const auto direct = !OnAdding()->HasHandlers();
84 {
85 std::unique_lock lock(mutex_);
86 if (!IsCompatible(object) || !CheckLoop(object)) {
87 return false;
88 }
89 for (auto it = children_.begin(); it != children_.end(); ++it) {
90 if (*it == object) {
91 // Already in the container, remove from current position
92 children_.erase(it);
93 break;
94 }
95 }
96 index = BASE_NS::Math::min(index, children_.size());
97 if (direct) {
98 children_.insert(children_.begin() + index, object);
99 }
100 }
101 // Calling external interface methods outside of our internal lock
102 bool success = true;
103 ChildChangedInfo info { object, index, parent_ };
104 if (!direct) {
105 Invoke<IOnChildChanging>(OnAdding(), info, success);
106 if (success) {
107 std::unique_lock lock(mutex_);
108 children_.insert(children_.begin() + index, object);
109 }
110 }
111 if (success) {
112 SetObjectParent(object, interface_pointer_cast<IObject>(parent_));
113 Invoke<IOnChildChanged>(OnAdded(), info);
114 }
115 return success;
116 }
117
Replace(const META_NS::IObject::Ptr & child,const META_NS::IObject::Ptr & replaceWith,bool addAlways)118 bool Container::Replace(const META_NS::IObject::Ptr& child, const META_NS::IObject::Ptr& replaceWith, bool addAlways)
119 {
120 IObject::Ptr removed;
121 IObject::Ptr added;
122 IObject::Ptr moved;
123 IContainer::SizeType fromIndex = 0;
124 IContainer::SizeType toIndex = 0;
125 bool changed = false; // True if any changes were made
126 {
127 std::unique_lock lock(mutex_);
128 if (replaceWith && (!IsCompatible(replaceWith) || !CheckLoop(replaceWith))) {
129 return false;
130 }
131 auto removedIt = children_.end();
132 auto movedIt = children_.end();
133 SizeType index = 0;
134 // Check if 'child' and 'replaceWith' can already be found from the container
135 for (auto it = children_.begin(); it != children_.end(); ++it, ++index) {
136 if (*it == child) {
137 removedIt = it;
138 removed = *it;
139 toIndex = index;
140 }
141 if (*it == replaceWith) {
142 movedIt = it;
143 moved = *it;
144 fromIndex = index;
145 }
146 if (removed && moved) {
147 break;
148 }
149 }
150 if (removed && removed == moved) {
151 // 'child' and 'replaceWith' are the same item (which is already in the container)
152 return true;
153 }
154 if (removed) {
155 // 'child' was found
156 if (replaceWith) {
157 *removedIt = replaceWith;
158 if (moved) {
159 // 'replaceWith' was already in the container, remove it from its previous position
160 children_.erase(movedIt);
161 } else {
162 // 'replaceWith' was added
163 added = replaceWith;
164 }
165 } else {
166 // 'replaceWith' == null, remove 'child'
167 children_.erase(removedIt);
168 }
169 changed = true;
170 } else {
171 moved.reset();
172 }
173 if (!changed && addAlways && replaceWith && movedIt == children_.end()) {
174 // 'child' was not found in container but addAlways is specified with a valid new item which
175 // was not previously in the container, add it
176 toIndex = children_.size();
177 children_.push_back(replaceWith);
178 added = replaceWith;
179 changed = true;
180 }
181 }
182 if (changed) {
183 // We did some changes to the container
184 ChildChangedInfo addedInfo { added, toIndex, parent_ };
185 ChildChangedInfo removedInfo { removed, toIndex, parent_ };
186 bool success = true;
187 // Replace operation does not support
188 if (removed) {
189 Invoke<IOnChildChanging>(OnRemoving(), removedInfo, success);
190 if (!success) {
191 CORE_LOG_E("Failing a remove transaction during replace operation is not supported");
192 success = true;
193 }
194 }
195 if (added) {
196 Invoke<IOnChildChanging>(OnAdding(), addedInfo, success);
197 if (!success) {
198 CORE_LOG_E("Failing an add transaction during replace operation is not supported");
199 }
200 }
201 SetObjectParent(removed, nullptr);
202 SetObjectParent(added, interface_pointer_cast<IObject>(parent_));
203 if (removed) {
204 Invoke<IOnChildChanged>(OnRemoved(), removedInfo);
205 }
206 if (added) {
207 Invoke<IOnChildChanged>(OnAdded(), addedInfo);
208 }
209 if (moved) {
210 Invoke<IOnChildMoved>(OnMoved(), ChildMovedInfo { moved, fromIndex, toIndex, parent_ });
211 }
212 }
213 return changed;
214 }
215
SetObjectParent(const IObject::Ptr & object,const IObject::Ptr & parent) const216 void Container::SetObjectParent(const IObject::Ptr& object, const IObject::Ptr& parent) const
217 {
218 const auto set = interface_cast<IMutableContainable>(object);
219 if (!set) {
220 // Object does not support setting a parent
221 return;
222 }
223 if (const auto cont = interface_cast<IContainable>(object)) {
224 // Remove from old parent (if any)
225 if (const auto old = interface_pointer_cast<IContainer>(cont->GetParent())) {
226 if (old == interface_pointer_cast<IContainer>(parent)) {
227 // The object is already a child of the new parent container
228 return;
229 }
230 old->Remove(object);
231 }
232 }
233 set->SetParent(parent);
234 }
235
CheckLoop(const IObject::Ptr & object) const236 bool Container::CheckLoop(const IObject::Ptr& object) const
237 {
238 // Check if adding object to "this" would lead to a loop in the hierarchy.
239 // This can only happen if object itself is a container which happens to be
240 // an ancestor of ours.
241 if (const auto cc = interface_cast<const IContainer>(object)) {
242 if (const auto me = interface_cast<IObjectInstance>(impl_)) {
243 if (cc->IsAncestorOf(me->GetSelf())) {
244 CORE_LOG_E("Adding '%s' to '%s' would lead to a loop in the container", object->GetName().c_str(),
245 me->GetName().c_str());
246 return false;
247 }
248 }
249 }
250 return true;
251 }
252
253 META_END_NAMESPACE()
254