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 return Container::Insert(-1, object);
42 }
43
Insert(SizeType index,const IObject::Ptr & object)44 bool Container::Insert(SizeType index, const IObject::Ptr& object)
45 {
46 if (!object) {
47 return false;
48 }
49 {
50 std::unique_lock lock(mutex_);
51 if (!IsCompatible(object) || !CheckLoop(object)) {
52 return false;
53 }
54 if (auto it = std::find(children_.begin(), children_.end(), object); it != children_.end()) {
55 // Already in the container, fail
56 return false;
57 }
58 index = BASE_NS::Math::min(index, children_.size());
59 children_.insert(children_.begin() + index, object);
60 }
61 ChildChangedInfo info { ContainerChangeType::ADDED, object, parent_, size_t(-1), index };
62 SetObjectParent(object, interface_pointer_cast<IObject>(parent_));
63 Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING), info);
64 return true;
65 }
66
Replace(const META_NS::IObject::Ptr & child,const META_NS::IObject::Ptr & replaceWith,bool addAlways)67 bool Container::Replace(const META_NS::IObject::Ptr& child, const META_NS::IObject::Ptr& replaceWith, bool addAlways)
68 {
69 IObject::Ptr removed;
70 IObject::Ptr added;
71 IObject::Ptr moved;
72 IContainer::SizeType fromIndex = 0;
73 IContainer::SizeType toIndex = 0;
74 bool changed = false; // True if any changes were made
75 {
76 std::unique_lock lock(mutex_);
77 if (replaceWith && (!IsCompatible(replaceWith) || !CheckLoop(replaceWith))) {
78 return false;
79 }
80 auto removedIt = children_.end();
81 auto movedIt = children_.end();
82 SizeType index = 0;
83 // Check if 'child' and 'replaceWith' can already be found from the container
84 for (auto it = children_.begin(); it != children_.end(); ++it, ++index) {
85 if (*it == child) {
86 removedIt = it;
87 removed = *it;
88 toIndex = index;
89 }
90 if (*it == replaceWith) {
91 movedIt = it;
92 moved = *it;
93 fromIndex = index;
94 }
95 if (removed && moved) {
96 break;
97 }
98 }
99 if (removed && removed == moved) {
100 // 'child' and 'replaceWith' are the same item (which is already in the container)
101 return true;
102 }
103 if (removed) {
104 // 'child' was found
105 if (replaceWith) {
106 *removedIt = replaceWith;
107 if (moved) {
108 // 'replaceWith' was already in the container, remove it from its previous position
109 children_.erase(movedIt);
110 } else {
111 // 'replaceWith' was added
112 added = replaceWith;
113 }
114 } else {
115 // 'replaceWith' == null, remove 'child'
116 children_.erase(removedIt);
117 }
118 changed = true;
119 } else {
120 moved.reset();
121 }
122 if (!changed && addAlways && replaceWith && movedIt == children_.end()) {
123 // 'child' was not found in container but addAlways is specified with a valid new item which
124 // was not previously in the container, add it
125 toIndex = children_.size();
126 children_.push_back(replaceWith);
127 added = replaceWith;
128 changed = true;
129 }
130 }
131 if (changed) {
132 // We did some changes to the container
133 ChildChangedInfo addedInfo { ContainerChangeType::ADDED, added, parent_, size_t(-1), toIndex };
134 ChildChangedInfo removedInfo { ContainerChangeType::REMOVED, removed, parent_, toIndex };
135 SetObjectParent(removed, nullptr);
136 SetObjectParent(added, interface_pointer_cast<IObject>(parent_));
137 if (removed) {
138 Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING), removedInfo);
139 }
140 if (added) {
141 Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING), addedInfo);
142 }
143 if (moved) {
144 Invoke<IOnChildChanged>(EventOnContainerChanged(MetadataQuery::EXISTING),
145 ChildChangedInfo { ContainerChangeType::MOVED, moved, parent_, fromIndex, toIndex });
146 }
147 }
148 return changed;
149 }
150
SetObjectParent(const IObject::Ptr & object,const IObject::Ptr & parent) const151 void Container::SetObjectParent(const IObject::Ptr& object, const IObject::Ptr& parent) const
152 {
153 const auto set = interface_cast<IMutableContainable>(object);
154 if (!set) {
155 // Object does not support setting a parent
156 return;
157 }
158 if (const auto cont = interface_cast<IContainable>(object)) {
159 // Remove from old parent (if any)
160 if (const auto old = interface_pointer_cast<IContainer>(cont->GetParent())) {
161 if (old == interface_pointer_cast<IContainer>(parent)) {
162 // The object is already a child of the new parent container
163 return;
164 }
165 old->Remove(object);
166 }
167 }
168 set->SetParent(parent);
169 }
170
CheckLoop(const IObject::Ptr & object) const171 bool Container::CheckLoop(const IObject::Ptr& object) const
172 {
173 // Check if adding object to "this" would lead to a loop in the hierarchy.
174 // This can only happen if object itself is a container which happens to be
175 // an ancestor of ours.
176 if (const auto cc = interface_cast<const IContainer>(object)) {
177 if (const auto me = interface_cast<IObjectInstance>(impl_)) {
178 if (cc->IsAncestorOf(me->GetSelf())) {
179 CORE_LOG_E("Adding '%s' to '%s' would lead to a loop in the container", object->GetName().c_str(),
180 me->GetName().c_str());
181 return false;
182 }
183 }
184 }
185 return true;
186 }
187
188 META_END_NAMESPACE()
189