• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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