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