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